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

相关推荐
廖子默18 分钟前
提供html2canvas+jsPDF将HTML页面以A4纸方式导出为PDF后,内容分页时存在截断的解决思路
前端·pdf·html
AndyGoWei1 小时前
react react-router-dom history 实现原理,看这篇就够了
前端·javascript·react.js
小仓桑1 小时前
深入理解 JavaScript 中的 AbortController
前端·javascript
换个名字不能让人发现我在摸鱼1 小时前
裁剪保存的图片黑边问题
前端·javascript
小牛itbull1 小时前
Mono Repository方案与ReactPress的PNPM实践
开发语言·前端·javascript·reactpress
小宋10212 小时前
实现Excel文件和其他文件导出为压缩包,并导入
java·javascript·excel·etl
码喽哈哈哈2 小时前
day01
前端·javascript·html
mubeibeinv2 小时前
分页/列表分页
java·前端·javascript
林太白2 小时前
js属性-IntersectionObserver
前端·javascript
爱吃羊的老虎2 小时前
【WEB开发.js】getElementById :通过元素id属性获取HTML元素
前端·javascript·html