Chrome 如何从选定的元素中创建查询选择器?

cbjzeqam  于 12个月前  发布在  Go
关注(0)|答案(2)|浏览(106)

在google Chrome中,尤其是现在的自定义元素,现在手工选择和元素变得非常麻烦,即使浏览器已经知道了它的整个路径。或者有没有一种方法可以为我正在检查的元素选择一个查询?

情况:

Chrome可以告诉我:

Chrome无法为我创建AFAIK:

6yt4nkrj

6yt4nkrj1#

在构建Chrome扩展时,我发现在返回页面时需要唯一地定位元素。要做到这一点,我需要创建一个查询字符串为选定的元素(自定义上下文菜单点击)
在寻找答案的过程中,我发现了一个没有答案的问题。
由于我找不到现成的解决方案或API来完成任务,我编写了以下函数。它未经测试,非常粗糙和现成(使用可怜的节点遍历技术)。我把它贴在这个州,以免我忘记,这个问题仍然没有答案。

创建元素查询字符串

用于构建查询字符串的函数,该查询字符串将从元素的引用中唯一定位元素。

const querytStr = createQueryStringForElement(myElement); // return string or undefined
if (querytStr) {
    const element = document.querySelector(queryStr);
    console.log(element === myElement); // expected result true
}

如果函数无法创建唯一定位元素的查询,则返回undefined。否则返回查询字符串。

示例结果

"#editor > div.ace_scroller > div.ace_content > div.ace_layer.ace_text-layer > div.ace_line:nth-child(45) > span.ace_punctuation.ace_operator"

"#buttons"                             // A UI container
"#buttons > div.buttons"               // A sub UI container
"#buttons > div.buttons:nth-child(2)"  // A button element by position
"#buttons > div.buttons:nth-child(3)"  // A button element by position

工作原理

  • 代码假设页面是格式良好的(id必须是唯一的)。
  • 查询字符串将尝试以id(例如"#elementId")开头,但如果元素没有id,则查询将使用标签和类名。例如"div.my-class"

标记和类名不能唯一标识元素。要检查查询是否唯一,可以使用查询字符串从元素父级查询DOM。
如果需要,查询字符串将使用元素位置来细化查询"div.my-class:nth-child(2)"。不幸的是,这使得结果查询字符串对元素顺序的更改不敏感。

  • 查询字符串沿着每个父元素构建,直到它找到一个带有id的元素,或者没有更多的父元素。
  • 最后一步使用查询查看查询是否找到了正确的元素,如果成功则返回查询。

代码

function createQueryStringForElement(element) {
    const getElementSel = element => {
        const tName = element.tagName.toLowerCase();
        var i = 0, str = element.id ? "#" + element.id : sel = tName;
        if (str.includes("#")) { return str}
        str += element.classList.length ? "." + [...element.classList.values()].join(".") : "";
        if (element.parentElement) {
            const res = element.parentElement.querySelector(str);
            if (res !== element) {
                while (i < element.parentElement.children.length) {
                    if (element.parentElement.children[i] === element) {
                        i > 0 && (str += ":nth-child(" + (i + 1) + ")" );
                        break;
                    }
                    i++;
                }
            }
        }
        return str;
    }
    const queryPath = [];
    const original = element;
    do {
        const subQuery = getElementSel(element);
        queryPath.push(subQuery);
        if (subQuery[0] === "#") { break }
        element = element.parentElement;
    } while (element);
    const query = queryPath.reverse().join(" > ");
    try {
        const els = document.querySelector(query);
        if (els === original) { return query }
    } catch(e) { }
}
pn9klfpd

pn9klfpd2#

我遇到了同样的问题,最后编写了一个简单的递归解决方案。如果布局是动态的或层次结构发生了变化,这将不起作用,但对于大多数静态站点,这很有效

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

  const tagName = element.tagName.toLowerCase();
  const parent = element.parentElement;
  const index = Array.from(parent.children).findIndex((child) => child === element);
  return `${this.constructQueryString(parent)} > ${tagName}:nth-child(${index + 1})`;
}

您可以通过以下方式进行测试:

document.querySelector(constructQueryString(element)) === element

相关问题