一般在开发时不会遇见这种需求,起因也是加入到一家趋于原生开发的公司,开发chrome插件时,遇到了这种场景,选择页面中所有的video标签。我们需要考虑Shadow DOM
, iframe
中等等情况。简单的通过document.querySelectorAll('video')
,他处理当前文档的元素,不能处理Shadow DOM
, iframe
等情况,下面就来介绍一下吧。
我们通过document.querySelectorAll('video')
去进行操作时,他不会去选择通过Shadow DOM
定义的自定义元素。如果想选中这种元素内部的内容,我们只能通过当前节点的shadowRoot
去获取Shadow DOM定义的跟标签再进行获取。具体请看这里
一般情况下,对于网站的video,我们可以直接获取。但是对于通过 Shadow DOM 定义的标签来说,我们不能直接通过document.querySelectorAll('video')
获取到该页面内所有的video元素。我们需要找到自定义标签,然后获取shadow dom根标签在内部进行查找。
// 例如
if(location.href.includes("douyu.com")) {
currentVideos = selectAll("demand-video")?.map(item => item.shadowRoot?.querySelector("video"))
}
但是这种也是硬编码。只能根据不同网站去做不同处理。可以使用query-selector-shadow-dom 来查找页面所有video标签包括shadow dom中的。
querySelectorAllDeep("video")
但是这个库有个缺点就是不能获取到iframe中对应的元素。所以我们还是需要通过document.querySelectorAll()
来获取。然后进行过滤。
let currentVideos = [...new Set([...querySelectorAllDeep("video"), ...selectAll<HTMLVideoElement>("video")])];
但是由于我们在manifest中设置了将当前脚本注入到iframe中,所以就不需要去获取iframe中的video了。
"content_scripts": [
{
"css": ["page.css"],
"js": ["page.js"],
"matches": ["*://*/*"],
"run_at": "document_idle",
"all_frames": true
}
],
// 查找页面video, querySelectorAllDeep获取不到iframe中的内容。但是我们通过`"all_frames": true`将该脚本注入到iframe中,所以不需要单独获取iframe中的video
const currentVideos = querySelectorAllDeep("video")
其实这个库的核心原理就是根据传入的根元素收集其内部的所有元素 包括嵌套元素。(这个根元素可能是一个shadow dom 自定义的元素,所以我们需要通过root.shadowRoot
去判断)。然后再进行遍历查找每个元素内部是否存在shadow dom
,如果有在进行元素的收集。
最后我们通过Element.matches()
方法根据传入的选择器查找到对应的目标元素。
function collectAllElementsDeep(selector = null, root = document) {
const allElements = [];
// 遍历所有元素
const findAllElements = function(nodes) {
for (let i = 0; i < nodes.length; i++) {
const el = nodes[i];
allElements.push(el);
// If the element has a shadow root, dig deeper.
if (el.shadowRoot) {
findAllElements(el.shadowRoot.querySelectorAll('*'));
}
}
};
// 查找当前元素是否是自定义元素。若是在继续加入当前自定义元素下的所有元素
if(root.shadowRoot) {
findAllElements(root.shadowRoot.querySelectorAll('*'));
}
// 加入跟标签下的所有元素
findAllElements(root.querySelectorAll('*'));
// 如果元素被指定的选择器字符串选择,Element.matches() 方法返回 true; 否则返回 false。
return selector ? allElements.filter(el => el.matches(selector)) : allElements;
}
xdm,已经快一个月没写框架了,写原生还是了解很多内容。现在也在通读一下mdn文档。感觉还有很多东西不了解!!!
往期年度总结
往期文章
- 因为原生,选择一家公司(前端如何防笔试作弊)
- 结合开发,带你熟悉package.json与tsconfig.json配置
- 如何优雅的在项目中使用echarts
- 如何优雅的做项目国际化
- 近三个月的排错,原来的憧憬消失喽
- 带你从0开始了解vue3核心(运行时)
- 带你从0开始了解vue3核心(computed, watch)
- 带你从0开始了解vue3核心(响应式)
- 3w+字的后台管理通用功能解决方案送给你
- 入职之前,狂补技术,4w字的前端技术解决方案送给你(vue3 + vite )
专栏文章
原文链接:https://juejin.cn/post/7311880893841752098 作者:Spirited_Away