Javascript 是否存在反向查询选择器?

Javascript 是否存在反向查询选择器?,javascript,firefox,dom,Javascript,Firefox,Dom,我知道我可以使用querySelector查找文档中的元素 var element = document.querySelector(".myclass") 但是否存在反向查询选择器,以便: var selector = document.inverseQuerySelector(element); Assert.AreEqual(element, document.querySelector(selector)); inverseQuerySelector返回的选择器始终唯一标识指定的元素

我知道我可以使用querySelector查找文档中的元素

var element = document.querySelector(".myclass")
但是否存在反向查询选择器,以便:

var selector = document.inverseQuerySelector(element);
Assert.AreEqual(element, document.querySelector(selector));

inverseQuerySelector返回的选择器始终唯一标识指定的元素?

否,因为有许多选择器(可能是无限的)可以选择相同的元素

要使一个函数是可逆的(即使在数学中),它的映射必须是1:1,事实并非如此

顺便说一句,正因为如此,您可以创建一些仅在某些情况下有效的。例如:

function inverseForElementWithUniqueId(element){
   return '#' + element.id;
}

var selector = inverseForElementWithUniqueId(element);
Assert.AreEqual(element, document.querySelector(selector));   //True
(看起来确实微不足道的代码)

但正如所说的,由于理论的原因,这只适用于部分案例


但是,它只有在某些情况下才会起作用。您可以创建一个在所有情况下都能起作用的。以两种方式:

  • 使用第n个子项(使用索引)
  • 解决方案如下:

    function getMyPathByIndex(element){
    
            if(element == null)
                return '';
    
            if(element.parentElement == null)
                return 'html'
    
            return getMyPathByIndex(element.parentElement) + '>' + ':nth-child(' + getMyIndex(element) + ')';
    
        }
    
    function getMyIndex(element){
    
            if(element == null)
                return -1;
            if(element.parentElement == null)
                return 0;
    
            let parent = element.parentElement;
    
            for(var index = 0; index < parent.childElementCount; index++)
                if(parent.children[index] == element)
                    return index + 1;
    
    }
    
    具有唯一的查询选择器:

    第n个孩子(2)>:第n个孩子(5)>:第n个孩子(1)>:第n个孩子(1)>:第n个孩子(3)>:第n个孩子(2)>:第n个孩子(6)>:第n个孩子(1)>:第n个孩子(1)>:第n个孩子(1)>:第n个孩子(1)>:第n个孩子(2)

  • 以“人类可读”的方式使用属性:
  • 获取元素的属性(仅显式属性)
  • 获取元素的整个路径
  • 使用多属性选择器统一所有要素
  • 使用相同的元素之前,它具有唯一的查询选择器:

    <a id="y" class="vote-up-off" title="This answer is useful">up vote</a> 
    
    html>body>div>div>div>div>div>div>table>tbody>tr>td>div>a[id=“y”][class=“投票结束”][title=“此 答案很有用“]

    通过以下方法测试溶液是否正确

    // e is an element and e must exist inside the page
    document.querySelector( getMyPathByIndex(e)) === e && 
    document.querySelectorAll( getMyPathByIndex(e)).length === 1
    
    // e is an element and e must exist inside the page
    document.querySelector( myUniqueQuerySelector(e)) === e && 
    document.querySelectorAll( myUniqueQuerySelector(e)).length === 1
    
    代码解决方案如下:

    function convertAttributesToQuerySelector(element){
    
      var tagName = element.tagName.toLowerCase();
      var result = tagName;   
    
      Array.prototype.slice.call(element.attributes).forEach( function(item) {
      if(element.outerHTML.contains(item.name))
        result += '[' + item.name +'="' + item.value + '"]';
    
    });
    
      return result;
      //["a[id="y"]", "a[class="vote-up-off"]", "a[title="This answer is useful"]"]
    
    }
    
    function getMyPath(element){
    
       if(element.parentElement.tagName == 'HTML')
        return 'html';
    
      return  getMyPath(element.parentElement) + '>' + element.parentElement.tagName.toLowerCase() ;
      //"html>body>div>div>div>div>div>div>table>tbody>tr>td>div"
    
    }
    
    function myUniqueQuerySelector(element){
    
      var elementPath = getMyPath(element);
      var simpleSelector =   convertAttributesToQuerySelector(element);
    
      return elementPath + '>' + simpleSelector;
    
    }
    
    您始终可以通过以下方法测试解决方案是否正确:

    // e is an element and e must exist inside the page
    document.querySelector( getMyPathByIndex(e)) === e && 
    document.querySelectorAll( getMyPathByIndex(e)).length === 1
    
    // e is an element and e must exist inside the page
    document.querySelector( myUniqueQuerySelector(e)) === e && 
    document.querySelectorAll( myUniqueQuerySelector(e)).length === 1
    

    我混合了建议的两种解决方案,以获得人类可读的结果,如果有几个类似的兄弟姐妹,则给出正确的元素:

    功能elemToSelector(elem){
    常数{
    标记名,
    身份证件
    类名,
    返回节点
    }=元素;
    如果(标记名=='HTML')返回'HTML';
    设str=标记名;
    str+=(id!='')`${id}`:'';
    if(类名){
    const classes=className.split(/\s/);
    for(设i=0;i${str}`;
    }
    
    使用以下各项进行测试:

    //在navigator Devtools的“元素”选项卡中选择一个元素,或替换$0
    document.querySelector(elemToSelector($0))=$0&&
    document.querySelectorAll(elemToSelector($0)).length==1
    
    这可能会让你觉得,它有点长,但可读性强,而且总是有效的:

    HTML > BODY:nth-child(2) > DIV.container:nth-child(2) > DIV.row:nth-child(2) > DIV.col-md-4:nth-child(2) > DIV.sidebar:nth-child(1) > DIV.sidebar-wrapper:nth-child(2) > DIV.my-4:nth-child(1) > H4:nth-child(3)
    

    编辑:我刚刚发现了这个包

    请尝试“not:”,如下面的问题所示回答:也许inverseQuerySelector是个坏名字。我想我所追求的是生成唯一标识文档中给定元素的选择器,而不是与任何可能的选择器相反的选择器。鉴于此,我扩展了答案您可以基于类、id和dom位置构建一个完整的查询,但我不认为这对真正好的答案有什么好处!在第一个选项中,如果元素具有id或其他唯一属性,是否可以缩短查询?@Giuppox如果您有id,查询选择器为
    #
    ,如果您具有唯一属性或具有唯一值的属性,则可以使用
    标记[attribute='regular-expresion']
    。更多信息请点击这里()