遍历DOM树并获取每个元素的位置信息

DOM(Document Object Model)树代表了文档的层次结构,它允许开发者通过编程方式访问和修改HTML或XML文档的内容。当我们需要对DOM树中的元素进行批量操作或数据分析时,遍历DOM树就显得尤为重要。

下面提供的 DOMIndexer 类代码完成了对可见元素进行索引。如下所示:

js 复制代码
class DOMIndexer {
  constructor() {
    if (DOMIndexer.instance) {
      return DOMIndexer.instance;
    }
    DOMIndexer.instance = this;
  }

  isElementVisible(element) {
    return window.getComputedStyle(element).display !== 'none';
  }

  traverseAndIndexVisibleElements(parentNode, cb) {
    if(this.isElementVisible(parentNode)){
      cb(parentNode);
      const children = parentNode.children;
      if(children?.length){
        for (let i = 0; i < children.length; i++) {
          const child = children[i];
          this.traverseAndIndexVisibleElements(child, cb);
        }
      }
    }
  }

  detachAndReattach(rootNode, callback, detach=true) {
    if(!detach){
      callback();
      return;
    }
    // 创建一个占位元素,用于在根元素脱离文档时保持其位置  
    const placeholder = document.createElement('div');

    // 插入占位符到根节点的位置  
    const parent = rootNode.parentNode;
    parent.insertBefore(placeholder, rootNode);

    // 脱离根元素  
    parent.removeChild(rootNode);

    // 设置一个标志,表示根节点已被脱离  
    rootNode.detached = true;

    // 执行回调,这里是遍历和索引DOM的操作  
    callback();

    // 删除标志  
    delete rootNode.detached;

    // 重新将根节点插入到文档中,替换占位元素  
    parent.replaceChild(rootNode, placeholder);
  }

  indexDOM(cb, root = document.body) {
    this.detachAndReattach(root, () => {
      // 遍历并索引可见元素  
      this.traverseAndIndexVisibleElements(root, cb);
    }, false);
  }

}

// 实现单例模式  
let instance = null;
let index = 0;

const exec = (element) => {
  element.setAttribute('data-index', index++);
}

DOMIndexer.getInstance = function () {
  if (!instance) {
    instance = new DOMIndexer();
  }
  return instance;
};

// 使用示例  
const indexer = DOMIndexer.getInstance();
indexer.indexDOM(exec);

下面介绍这段代码的实现原理:

1. DOMIndexer类简介

DOMIndexer类是一个实现了单例模式的工具类,用于遍历和索引DOM树中的可见元素。它提供了几个关键方法,包括判断元素是否可见、遍历并索引可见元素,以及在遍历过程中临时脱离和重新附加根节点,以减少重排和重绘的性能开销。

2. isElementVisible方法

这个方法用于判断一个DOM元素是否可见。它利用window.getComputedStyle方法来获取元素的计算样式,并检查display属性是否不等于none。如果display不是none,则说明元素是可见的。

3. traverseAndIndexVisibleElements方法

这是DOMIndexer类的核心方法之一。它是一个递归函数,从给定的父节点开始,遍历其所有子节点。对于每个可见的子节点,它都会调用传入的回调函数cb,并将该节点作为参数传递。然后,它会递归地对每个子节点执行相同的操作。

4. detachAndReattach方法

在遍历DOM树之前,detachAndReattach方法会先将根节点从文档中临时脱离,以减少浏览器在遍历过程中的重排和重绘次数。脱离后,它会执行传入的回调函数(通常是遍历和索引操作),然后再将根节点重新附加到文档中。

5. indexDOM方法

这是对外暴露的主要方法,用于启动整个遍历和索引过程。它接受一个回调函数作为参数,这个回调函数定义了如何对每个可见元素进行索引。默认情况下,遍历的起点是文档的body元素。

6. 使用示例

示例中首先通过DOMIndexer.getInstance()获取DOMIndexer类的单例实例。然后定义一个名为exec的回调函数,该函数简单地为每个可见元素设置一个data-index属性,并使用一个全局变量index来递增赋值。最后调用indexer.indexDOM(exec)来启动遍历和索引过程。

通过DOMIndexer类,可以高效地遍历DOM树中的可见元素,并对它们进行自定义操作(如索引、修改等)。这种遍历方式在处理大型DOM树时尤为有用,因为它可以显著减少浏览器的重排和重绘次数,从而提高性能。同时,由于采用了单例模式,可以确保在整个应用中只有一个DOMIndexer实例,从而节省内存资源。

应用

使用上面提供的 DOMIndexer 类对指定DOM元素及其所有子元素的位置信息进行统计。

js 复制代码
let instance = null;
let index = 0;
const elementsInfo = {}; 
const exec = (element) => {  
  // 设置 data-index 属性  
  element.setAttribute('data-index', index++);  
  
  // 获取元素的宽高、top 和 left 值  
  const rect = element.getBoundingClientRect();  
  const position = getComputedStyle(element).position;
  const width = rect.width;  
  const height = rect.height;  
  const top = rect.top;  
  const left = rect.left;  
  
  // 以 data-index 的值为键,将统计值存放在结构体中  
  const dataIndex = element?.getAttribute('data-index');  
  elementsInfo[dataIndex] = { width, height, top, left, position };  
}; 



DOMIndexer.getInstance = function () {
  if (!instance) {
    instance = new DOMIndexer();
  }
  return instance;
};

// 使用示例  
const indexer = DOMIndexer.getInstance();
indexer.indexDOM(exec);

console.log('elementsInfo:', elementsInfo)

exec回调函数与元素信息收集

示例中定义了一个名为exec的回调函数。这个函数对每个可见元素执行以下操作:

  • 设置一个data-index属性,用于唯一标识该元素。
  • 使用getBoundingClientRect方法获取元素的布局信息,如宽度、高度、顶部和左边的位置。
  • 使用getComputedStyle方法获取元素的定位信息。
  • 将这些信息存储在一个全局对象elementsInfo中,以便后续使用。

应用场景

这种遍历DOM树并收集元素信息的方法在多个场景中都非常有用。例如:

  • 性能分析:通过收集页面上所有可见元素的布局信息,可以分析哪些元素占用了过多的空间或导致页面加载缓慢。
  • 自动化测试:在自动化测试脚本中,可以使用这种方法来验证页面上的元素是否按预期显示和布局。
  • 动态样式调整:根据收集到的元素信息,可以动态地调整页面的样式,以提供更好的用户体验。

总结

通过DOMIndexer类可以高效地遍历DOM树中的可见元素,并收集它们的布局信息。这种方法在Web开发中具有广泛的应用价值,无论是用于性能分析、自动化测试还是动态样式调整。

相关推荐
fishmemory7sec1 分钟前
Electron 使⽤ electron-builder 打包应用
前端·javascript·electron
JUNAI_Strive_ving1 小时前
番茄小说逆向爬取
javascript·python
看到请催我学习1 小时前
如何实现两个标签页之间的通信
javascript·css·typescript·node.js·html5
twins35202 小时前
解决Vue应用中遇到路由刷新后出现 404 错误
前端·javascript·vue.js
qiyi.sky2 小时前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~2 小时前
分析JS Crash(进程崩溃)
java·前端·javascript
哪 吒2 小时前
华为OD机试 - 几何平均值最大子数(Python/JS/C/C++ 2024 E卷 200分)
javascript·python·华为od
安冬的码畜日常2 小时前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺
l1x1n03 小时前
No.3 笔记 | Web安全基础:Web1.0 - 3.0 发展史
前端·http·html
Q_w77423 小时前
一个真实可用的登录界面!
javascript·mysql·php·html5·网站登录