如何获取页面中指定的全部元素

一般在开发时不会遇见这种需求,起因也是加入到一家趋于原生开发的公司,开发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根标签在内部进行查找。

js 复制代码
// 例如
    if(location.href.includes("douyu.com")) {
      currentVideos = selectAll("demand-video")?.map(item => item.shadowRoot?.querySelector("video"))
    }

但是这种也是硬编码。只能根据不同网站去做不同处理。可以使用query-selector-shadow-dom 来查找页面所有video标签包括shadow dom中的。

js 复制代码
querySelectorAllDeep("video")

但是这个库有个缺点就是不能获取到iframe中对应的元素。所以我们还是需要通过document.querySelectorAll()来获取。然后进行过滤。

js 复制代码
 let currentVideos = [...new Set([...querySelectorAllDeep("video"), ...selectAll<HTMLVideoElement>("video")])];

但是由于我们在manifest中设置了将当前脚本注入到iframe中,所以就不需要去获取iframe中的video了。

js 复制代码
"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()方法根据传入的选择器查找到对应的目标元素。

js 复制代码
 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文档。感觉还有很多东西不了解!!!

往期年度总结

往期文章

专栏文章

相关推荐
程序员黑豆2 小时前
Java包装类:基本类型与对象的桥梁
java·前端·ai编程
ShineWinsu4 小时前
对于C++:auto_ptr、unique_ptr、shared_ptr的模拟实现
c++·面试·笔试·智能指针·unique_ptr·shared_ptr·auto_ptr
bugcome_com5 小时前
JWT 知识小课堂:从入门到精通,一文吃透 JSON Web Token
前端·json
To_OC5 小时前
写了三天 Next.js,我的 React 世界观被拆了又装回去
前端·全栈·next.js
独泪了无痕5 小时前
viewer.js 安装与配置指南:实现图片预览功能
前端·vue.js
起床学FPGA7 小时前
AI回答问题之后,会自动跳到最下面的问题
开发语言·javascript·ecmascript
黄敬峰7 小时前
一文搞懂 NestJS 后端框架:工厂模式、模块化与装饰器
面试
Fluxart.ai7 小时前
电商商品图审核怎么自动化?规则引擎、人工复核与发布门禁
java·前端·自动化
why技术7 小时前
AI 写的文章,可能都带着手敲一遍都去不掉的“隐形水印”。
前端·人工智能·后端
愚公搬代码8 小时前
【愚公系列】《Android应用案例开发大全》016-LBS类应用掌上杭州(辅助工具类的开发)
android·前端