JavaScript querySelector 和 CSS :target 伪类

Posted

技术标签:

【中文标题】JavaScript querySelector 和 CSS :target 伪类【英文标题】:JavaScript querySelector and CSS :target pseudo-class 【发布时间】:2021-07-10 07:17:33 【问题描述】:

我正在尝试在文档加载后使用伪类:target 获取目标元素。 我创建了以下示例来说明问题。

<!DOCTYPE html>
<html>
    <head>
        <script>
            document.addEventListener("DOMContentLoaded",function()
                console.log(document.querySelector(":target"));
            );
        </script>
    </head>
    <body>
        <div id="test"></div>
    </body>
</html>

如果我加载test.html,那么控制台输出:

null

如果我在 Chrome 和 Opera 上加载 test.html#test,那么控制台会输出:

null

如果我在 Firefox 和 IE11 上加载 test.html#test,那么控制台输出:

<div id="test"></div>

我的问题是:

    哪些浏览器的行为正确? DOMContentLoaded 是调用querySelector(":target") 的正确事件吗? 是否有其他方法可以在文档加载后获取目标元素?

PS:感谢setTimeout,我成功解决了 Chrome 和 Opera 上的问题,但这不是一个好的解决方案。有人有更好的主意吗?

编辑:我发现与 JQuery Selecting :target on document.ready() 类似的问题

【问题讨论】:

来自Selectors API Level 1: "建议作者虽然允许在选择器中使用 伪元素,但它们不会匹配文档中的任何元素,因此会不会导致返回任何元素。因此,建议作者避免在传递给本规范中定义的方法的选择器中使用伪元素。" :target 是pseudo class 而不是pseudo element onload 中,它在 Chrome 中可以使用 fine for me,但在 Safari 中则不行...一个破解方法是在 :target 上设置一个小的过渡(0.01 秒)并收听过渡结束事件。 【参考方案1】:

这是一个known issue,带有基于 WebKit 和 Blink 的浏览器,从未被直接解决。 web-platform-tests 建议的解决方法是request an animation frame,它只发生在页面渲染之后,此时:target 伪似乎匹配成功:

async_test(function() 
  var frame = document.createElement("iframe");
  var self = this;
  frame.onload = function() 
    // :target doesn't work before a page rendering on some browsers.  We run
    // tests after an animation frame because it may be later than the first
    // page rendering.
    requestAnimationFrame(self.step_func_done(init.bind(self, frame)));
  ;
  frame.src = "ParentNode-querySelector-All-content.xht#target";
  document.body.appendChild(frame);
)

我的测试表明,简单地使用onload 可以正常工作,但作者可能正在做某事,此外,一次调用requestAnimationFrame() 几乎不需要任何费用,所以您不妨效仿。

以下测试使用onload(而不是DOMContentLoaded,后者在DOM 树构建完成后立即触发,但不一定呈现):

data:text/html,<!DOCTYPE html><script>window.onload=function()console.log(document.querySelector(":target"));;</script><div id="test"></div>#test

以下测试将requestAnimationFrame()onload结合使用:

data:text/html,<!DOCTYPE html><script>window.onload=requestAnimationFrame(function()console.log(document.querySelector(":target")););</script><div id="test"></div>#test

【讨论】:

关于第一段,似乎有必要在onload中调用requestAnimationFrame,至少对于Chrome来说,当然是因为crbug.com/1018120【参考方案2】:

看起来 Firefox 具有理想的行为,但可能不是正确的。

不过,作为替代方案,您可以使用:

document.addEventListener('DOMContentLoaded', () => document.querySelector(window.location.hash));

这将适用于所有浏览器。

【讨论】:

“看起来 Firefox 具有理想的行为...” - 违反规范如何是“理想行为” @Andreas - 规范既不是万无一失的也不是一成不变的。 如果我加载带有空 location.hash 的文档,我会收到错误 Uncaught DOMException。这意味着我需要在querySelector 之前添加一个检查,但它是:target 的一个很好的替代品。 回复:“也就是说我需要在querySelector之前添加一个检查”。正确的。您可以直接写如下内容:if (window.location.hash !== '') console.log(document.querySelector(window.location.hash)); else console.log('The current URL contains no hashfragment.'); . 这个 will 不会捕获 github.com/WICG/scroll-to-text-fragment(Chrome 中已经存在),因为 :target 匹配文本片段的父元素,但 location.hash 不是设置。

以上是关于JavaScript querySelector 和 CSS :target 伪类的主要内容,如果未能解决你的问题,请参考以下文章

捕获具有数据属性的 html 元素 - javascript ".querySelector"

Javascript .querySelector 通过 innerTEXT 查找 <div>

javascript高级选择器querySelector和querySelectorAll

JavaScript中querySelector()和getElementById()(getXXXByXX)的区别

javascript forEach for queryselector

前端强大的javascript原生选择器querySelector 和 querySelectorAll