【问题标题】:Does an Inverse querySelector exist?逆查询选择器是否存在?
【发布时间】:2014-01-28 08:59:48
【问题描述】:

我知道我可以使用 querySelector 来定位文档中的元素

var element = document.querySelector(".myclass")

但是是否存在这样的逆查询选择器:

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

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

【问题讨论】:

标签: javascript firefox dom


【解决方案1】:

您可以创建一种适用于所有情况的方法。两种方式:

  1. 使用 nth-child(正在使用索引)

解决方法如下:

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;

}

例如,元素

<a id="y" class="vote-up-off" title="This answer is useful">up vote</a> 

您可以在此页面中获取此元素只需在控制台中输入:

document.querySelector('a[title="This answer is useful"]');

有它独特的查询选择器

html>:nth-child(2)>:nth-child(5)>:nth-child(1)>:nth-child(1)>:nth-child(3)>:nth-child( 2)>:nth-child(6)>:nth-child(1)>:nth-child(1)>:nth-child(1)>:nth-child(1)>:nth-child(1) >:nth-child(2)

  1. 属性用于“人类可读”的方式
    1. 获取元素的属性(仅显式属性)。
    2. 获取元素的完整路径。
    3. 使用多属性选择器统一所有特征。

使用相同的元素之前有它唯一的querySelector

html>body>div>div>div>div>div>div>table>tbody>tr>td>div>a[id="y"][class="vote-up-off"][title ="这个 答案很有用”]

测试解决方案是否正确

// e is an element and e must exist inside the page
document.querySelector( getMyPathByIndex(e)) === e && 
document.querySelectorAll( getMyPathByIndex(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( myUniqueQuerySelector(e)) === e && 
document.querySelectorAll( myUniqueQuerySelector(e)).length === 1

【讨论】:

  • 非常好的答案!在第一个选项中,如果元素具有 id 或其他唯一属性,是否可以缩短查询?
  • @Giuppox 如果你有一个 id,查询选择器是#&lt;idvalue&gt;,如果你有一个唯一的属性或具有唯一值的属性,你可以使用tag[attribute='regular-expresion']。更多信息在这里(developer.mozilla.org/en-US/docs/Web/API/Element/…
【解决方案2】:

不,因为有很多选择器(可能是无限的)可以选择同一个元素。

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

顺便说一句,因此,您可以创建一些可能只在在某些情况下工作的东西。例如:

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

var selector = inverseForElementWithUniqueId(element);
Assert.AreEqual(element, document.querySelector(selector));   //True

(确实可能看起来微不足道的代码)

但如前所述,由于理论上的原因,这仅适用于部分情况。

但是,它只是有时会起作用

【讨论】:

  • 也许 inverseQuerySelector 是个坏名字。我想我所追求的只是可以生成一个唯一标识文档中给定元素的选择器,而不是任何可能的选择器的反面。
  • 鉴于此,我扩展了答案
  • 您可以根据类、id 和 dom 位置构建一个完整的查询,但我看不出这有什么好处
【解决方案3】:

我将提出的 2 种解决方案混合在一起,以得到人类可读的结果,并且如果有几个相似的兄弟姐妹,它会给出正确的元素:

function elemToSelector(elem) {
  const {
    tagName,
    id,
    className,
    parentNode
  } = elem;

  if (tagName === 'HTML') return 'HTML';

  let str = tagName;

  str += (id !== '') ? `#${id}` : '';

  if (className) {
    const classes = className.split(/\s/);
    for (let i = 0; i < classes.length; i++) {
      str += `.${classes[i]}`;
    }
  }

  let childIndex = 1;

  for (let e = elem; e.previousElementSibling; e = e.previousElementSibling) {
    childIndex += 1;
  }

  str += `:nth-child(${childIndex})`;

  return `${elemToSelector(parentNode)} > ${str}`;
}

测试:

// Select an element in Elements tab of your navigator Devtools, or replace $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)

编辑:我刚刚找到了包unique-selector

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-10-25
    • 1970-01-01
    • 1970-01-01
    • 2018-11-21
    • 2018-07-15
    • 2020-02-28
    • 1970-01-01
    • 2015-02-05
    相关资源
    最近更新 更多