详解原生JS实现offset方法的两种方案
本文详细解析了原生JavaScript中实现offset方法的两种主流方案。第一种通过递归遍历父节点累加位置,兼容性好但逻辑复杂;第二种利用getBoundingClientRect和getClientRects获取视口坐标,代码简洁但仅适用于现代浏览器。文章对比了两种方法的实现细节、适用场景及浏览器兼容性差异,帮助开发者根据实际需求选择合适方案。
原生JS offset方法实现概述
在为 jTool 库开发 offset 方法(用于获取当前节点相对于视口的位置)时,先后尝试了两种不同的实现路径。现将这两种方案的实现思路与代码整理如下,供后续参考。
上述两种实现方式各有优劣,下面将分别详细列出其代码逻辑与使用场景。
方案一:递归实现节点定位
方案一:通过递归实现
function offset(element) {
var offest = {
top: 0,
left: 0
};
var _position;
getOffset(element, true);
return offest;
// 递归获取 offset, 可以考虑使用 getBoundingClientRect
function getOffset(node, init) {
// 非Element 终止递归
if (node.nodeType !== 1) {
return;
}
_position = window.getComputedStyle(node)['position'];
// position=static: 继续递归父节点
if (typeof(init) === 'undefined' && _position === 'static') {
getOffset(node.parentNode);
return;
}
offest.top = node.offsetTop + offest.top - node.scrollTop;
offest.left = node.offsetLeft + offest.left - node.scrollLeft;
// position = fixed: 获取值后退出递归
if (_position === 'fixed') {
return;
}
getOffset(node.parentNode);
}
}
// 执行offset
var s_kw_wrap = document.querySelector('#s_kw_wrap');
offset(s_kw_wrap); // => Object {top: 181, left: 400}
方案二:基于ClientRect的现代浏览器实现
方案二:通过 ClientRect 实现
function offset2(node) {
var offest = {
top: 0,
left: 0
};
// 当前为IE11以下, 直接返回{top: 0, left: 0}
if (!node.getClientRects().length) {
return offest;
}
// 当前DOM节点的 display === 'node' 时, 直接返回{top: 0, left: 0}
if (window.getComputedStyle(node)['display'] === 'none') {
return offest;
}
// Element.getBoundingClientRect()方法返回元素的大小及其相对于视口的位置。
// 返回值包含了一组用于描述边框的只读属性——left、top、right和bottom,单位为像素。除了 width 和 height 外的属性都是相对于视口的左上角位置而言的。
// 返回如{top: 8, right: 1432, bottom: 548, left: 8, width: 1424…}
offest = node.getBoundingClientRect();
var docElement = node.ownerDocument.documentElement;
return {
top: offest.top + window.pageYOffset - docElement.clientTop,
left: offest.left + window.pageXOffset - docElement.clientLeft
};
}
// 执行offset
var s_kw_wrap = document.querySelector('#s_kw_wrap');
offset2(s_kw_wrap); // => Object {top: 181.296875, left: 399.5}
在 offset2() 函数的实现中,调用了 .getClientRects() 与 .getBoundingClientRect() 这两个 DOM API。需要注意的是,IE11 及更低版本的浏览器并不支持这些方法,因此该方案仅适用于现代浏览器环境。
getClientRects() 方法解析
.getClientRects() 方法详解
该方法返回一个 ClientRect 对象的集合,这些对象对应于元素相关的 CSS 边框。每个 ClientRect 对象包含一组只读属性:left、top、right 和 bottom,单位均为像素。这些坐标值是相对于视口左上角(top-left)计算的。
此外,返回结果还包含 length 属性。在 IE11 以下的浏览器中,可以通过检测该属性是否存在来验证当前是否为旧版 IE 浏览器。
getBoundingClientRect() 方法解析
.getBoundingClientRect() 方法详解
该方法返回一个包含描述边框的只读属性集合的对象,包括 left、top、right 和 bottom,单位为像素。除 width 和 height 外,这些属性值均相对于视口的左上角位置。
两个方法的区别与联系
.getBoundingClientRect() 与 .getClientRects() 的关系
- 两者的区别主要取决于元素的 display 属性。当 display 为 inline 时,.getClientRects() 会返回当前节点内每一行文本的 ClientRect 对象数组,此时数组的长度等于文本的行数。
- 当 display 不等于 inline 时,.getClientRects() 返回包含当前节点信息的 ClientRect 对象数组,此时数组长度固定为 1。
- .getBoundingClientRect() 始终返回当前节点的单个 ClientRect 对象,注意这里是单个对象而非对象数组。
提示
以上测试逻辑可以通过在百度首页执行代码进行验证,具体可通过 document.querySelector('#s_kw_wrap') 获取百度首页输入框的节点进行测试。
希望上述内容能对各位的学习有所帮助,也欢迎大家继续支持本站。
JS怎么使用classList添加、删除和切换class
2026-11-05 17:26:02
网页开发三者分工:HTML结构、CSS样式与JavaScript交互怎么协作
2026-11-05 12:51:22
JS解析URL参数时怎么处理中文乱码和特殊字符
2026-11-05 12:47:23
js-cookie 存储 JSON 对象:自动序列化与反序列化技巧
2026-11-05 12:36:26
marked.js 快速上手:安装与基础 Markdown 转 HTML 示例
2026-11-05 09:32:53
