admin管理员组

文章数量:1394133

I am trying to find a way to build the querySelector string of any given node. In other words - pick any node on a page - is it possible to walk up the DOM and build a string that would allow me to pass the generated string to document.querySelector and get back the node I chose?

From what I can tell querySelector has a bug where you can only use nth-child once in the string.

I have tried several times but so far have failed to find a solution. I want to do this in native JavaScript, not jQuery.Any suggestions?

I am trying to find a way to build the querySelector string of any given node. In other words - pick any node on a page - is it possible to walk up the DOM and build a string that would allow me to pass the generated string to document.querySelector and get back the node I chose?

From what I can tell querySelector has a bug where you can only use nth-child once in the string.

I have tried several times but so far have failed to find a solution. I want to do this in native JavaScript, not jQuery.Any suggestions?

Share Improve this question edited Apr 22, 2015 at 4:52 BoltClock 725k165 gold badges1.4k silver badges1.4k bronze badges asked Apr 22, 2015 at 4:48 Steve LloydSteve Lloyd 9592 gold badges20 silver badges39 bronze badges 13
  • 1 "From what I can tell querySelector has a bug where you can only use nth-child once in the string." I'm curious, where did you read this? Can you show us what you've tried? – BoltClock Commented Apr 22, 2015 at 4:50
  • 2 If the element doesn't have an id already, then generate a unique ID for it, put the id on the object and then use that with document.querySelector(). What problem are you really trying to solve? You can always just save an actual reference to the DOM element itself if you just want to be able to get back to it. – jfriend00 Commented Apr 22, 2015 at 4:52
  • 1 You can use xpath based node selection. document.evaluate in Chrome & FF and document.selectNodes in IE – Vijay Commented Apr 22, 2015 at 4:55
  • 2 @BoltClock: developer.mozilla/en/docs/Using_XPath#getXPathForElement – Amadan Commented Apr 22, 2015 at 5:03
  • 2 @Steve Lloyd: I think you can simply pass document directly as the second argument to getXPathForElement. – BoltClock Commented Apr 22, 2015 at 18:10
 |  Show 8 more ments

2 Answers 2

Reset to default 6

I see this question is from 2015 but I just dealt with this issue and had to build a custom function to do so.

I've made a snippet to test it, just click any element and the querySelector should display as a string in the bottom div.

function getQuerySelector(elem) {

  var element = elem;
  var str = "";

  function loop(element) {

    // stop here = element has ID
    if(element.getAttribute("id")) {
      str = str.replace(/^/, " #" + element.getAttribute("id"));
      str = str.replace(/\s/, "");
      str = str.replace(/\s/g, " > ");
      return str;
    }

    // stop here = element is body
    if(document.body === element) {
      str = str.replace(/^/, " body");
      str = str.replace(/\s/, "");
      str = str.replace(/\s/g, " > ");
      return str;
    }

    // concat all classes in "queryselector" style
    if(element.getAttribute("class")) {
      var elemClasses = ".";
      elemClasses += element.getAttribute("class");
      elemClasses = elemClasses.replace(/\s/g, ".");
      elemClasses = elemClasses.replace(/^/g, " ");
      var classNth = "";

      // check if element class is the unique child
      var childrens = element.parentNode.children;

      if(childrens.length < 2) {
        return;
      }

      var similarClasses = [];

      for(var i = 0; i < childrens.length; i++) {
        if(element.getAttribute("class") == 
childrens[i].getAttribute("class")) {
          similarClasses.push(childrens[i]);
        }
      }

      if(similarClasses.length > 1) {
        for(var j = 0; j < similarClasses.length; j++) {
          if(element === similarClasses[j]) {
            j++;
            classNth = ":nth-of-type(" + j + ")";
            break;
          }
        }
      }

      str = str.replace(/^/, elemClasses + classNth);

    }
    else{

      // get nodeType
      var name = element.nodeName;
      name = name.toLowerCase();
      var nodeNth = "";

      var childrens = element.parentNode.children;

      if(childrens.length > 2) {
        var similarNodes = [];

        for(var i = 0; i < childrens.length; i++) {
          if(element.nodeName == childrens[i].nodeName) {
            similarNodes.push(childrens[i]);
          }
        }

        if(similarNodes.length > 1) {
          for(var j = 0; j < similarNodes.length; j++) {
            if(element === similarNodes[j]) {
              j++;
              nodeNth = ":nth-of-type(" + j + ")";
              break;
            }
          }
        }

      }

      str = str.replace(/^/, " " + name + nodeNth);

    }

    if(element.parentNode) {
      loop(element.parentNode);
    }
    else {
      str = str.replace(/\s/g, " > ");
      str = str.replace(/\s/, "");
      return str;
    }

  }

  loop(element);

  return str;


}

https://jsfiddle/wm6goeyw/

This works as long as DOM structure remains same. Even little change in HTML code could result to incorrect (in terms of return value) result of this function. So, it is not intended for long storing of DOM reference to HTML element.

function createQuerySelector(element) {
    if (element.id) {
        return `#${element.id}`;
    }

    const path = [];
    let currentElement = element;
    let error = false;

    while (currentElement.tagName !== 'BODY') {
        const parent = currentElement.parentElement;

        if (!parent) {
            error = true;
            break;
        }

        const childTagCount= {};
        let nthChildFound = false;

        for (const child of parent.children) {
            const tag = child.tagName;
            const count = childTagCount[tag] || 0;
            childTagCount[tag] = count + 1;

            if (child === currentElement) {
                nthChildFound = true;
                break;
            }
        }

        if (!nthChildFound) {
            error = true;
            break;
        }

        const count = childTagCount[currentElement.tagName];
        const tag = currentElement.tagName.toLowerCase();
        const selector = `${tag}:nth-of-type(${count})`;

        path.push(selector);
        currentElement = parent;
    }

    if (error) {
        console.error(element);
        throw new Error('Unable to create query selector');
    }

    path.push('body');

    const querySelector = path.reverse().join(' > ');

    return querySelector;
}

本文标签: javascriptBuild the querySelector string value of any given node in the DOMStack Overflow