如何过滤网页上可见的HTML节点
在前端开发中,我们经常需要筛选出页面中用户可见的HTML节点,用于数据统计、内容提取、样式适配等场景。要准确过滤可见节点,首先需要明确“可见节点”的判定标准,再结合DOM API实现过滤逻辑。
可见节点的判定标准
通常符合以下条件的节点会被判定为可见:
节点的
display属性值不为none节点的
visibility属性值不为hidden或collapse节点的
opacity属性值大于0节点的宽度和高度都大于0
节点不在视口外(或者业务场景不需要考虑视口范围时可忽略该条件)
节点没有被其他元素完全遮挡(常规场景中可忽略该复杂条件,仅做基础判断即可)
基础过滤实现
我们可以通过遍历节点,结合getComputedStyle方法获取节点的计算样式,依次校验上述判定条件。以下是一个通用的过滤函数示例:
/**
* 判断单个节点是否为可见节点
* @param {HTMLElement} node - 待检测的DOM节点
* @param {boolean} checkViewport - 是否检查节点是否在视口内,默认false
* @returns {boolean} 可见返回true,不可见返回false
*/
function isVisibleNode(node, checkViewport = false) {
// 排除非元素节点
if (!(node instanceof HTMLElement)) {
return false;
}
const style = window.getComputedStyle(node);
// 检查display、visibility、opacity
if (style.display === 'none') return false;
if (style.visibility === 'hidden' || style.visibility === 'collapse') return false;
if (parseFloat(style.opacity) viewWidth || rect.bottom < 0 || rect.top > viewHeight) {
return false;
}
}
return true;
}
/**
* 过滤一组节点中的可见节点
* @param {NodeList|Array} nodes - 待过滤的节点集合
* @param {boolean} checkViewport - 是否检查节点是否在视口内,默认false
* @returns {Array} 可见节点数组
*/
function filterVisibleNodes(nodes, checkViewport = false) {
const result = [];
for (let i = 0; i < nodes.length; i++) {
if (isVisibleNode(nodes[i], checkViewport)) {
result.push(nodes[i]);
}
}
return result;
}使用示例
假设我们需要过滤页面中所有<div>标签里的可见节点,可以按照以下方式调用:
// 获取页面所有div节点
const allDivs = document.querySelectorAll('div');
// 过滤可见节点,不检查视口范围
const visibleDivs = filterVisibleNodes(allDivs);
console.log('可见的div节点数量:', visibleDivs.length);
// 如果需要同时检查节点是否在当前视口内
const visibleInViewportDivs = filterVisibleNodes(allDivs, true);
console.log('视口内可见的div节点数量:', visibleInViewportDivs.length);注意事项
在实际使用中需要注意以下几点:
如果节点被父元素设置为
display: none,子节点的计算样式也会返回display: none,无需额外递归检查父节点状态对于<script>、<style>、<meta>等非渲染类标签,即使获取到的宽高可能为0,也会在
instanceof HTMLElement校验环节被排除如果页面存在大量节点需要过滤,建议分批处理或者使用
requestIdleCallback避免阻塞主线程若需要访问示例网站测试效果,可访问https://www.ipipp.com查看相关DOM操作示例