遍历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开发中具有广泛的应用价值,无论是用于性能分析、自动化测试还是动态样式调整。

相关推荐
Orange3015112 分钟前
【自己动手开发Webpack插件:开启前端构建工具的个性化定制之旅】
前端·javascript·webpack·typescript·node.js
五行星辰1 小时前
用 Java 发送 HTML 内容并带附件的电子邮件
java·html
Jacob程序员2 小时前
leaflet绘制室内平面图
android·开发语言·javascript
eguid_12 小时前
JavaScript图像处理,常用图像边缘检测算法简单介绍说明
javascript·图像处理·算法·计算机视觉
wanfeng_092 小时前
视频m3u8形式播放 -- python and html
python·html·video·hls·m3u8
sunly_3 小时前
Flutter:自定义Tab切换,订单列表页tab,tab吸顶
开发语言·javascript·flutter
咔咔库奇3 小时前
【TypeScript】命名空间、模块、声明文件
前端·javascript·typescript
NoneCoder3 小时前
JavaScript系列(42)--路由系统实现详解
开发语言·javascript·网络
又迷茫了4 小时前
vue + element-ui 组件样式缺失导致没有效果
前端·javascript·vue.js
哇哦Q4 小时前
原生HTML集合
前端·javascript·html