详解原生js实现offset方法


Posted in Javascript onJune 15, 2017

在为 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实现

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() 方法,IE11 以下浏览器并不支持; 所以该种实现, 只适于现代浏览器。

.getClientRects()

返回值是 ClientRect 对象集合(与该元素相关的CSS边框),每个 ClientRect 对象包含一组描述该边框的只读属性——left、top、right 和 bottom,单位为像素,这些属性值是相对于视口的top-left的。

并包含 length 属性, IE11以下可以通过是否包含 length 来验证当前是否为IE11以上版现。

.getBoundingClientRect()

返回值包含了一组用于描述边框的只读属性——left、top、right 和 bottom,单位为像素。除了 width 和 height 外的属性都是相对于视口的左上角位置而言的。

.getBoundingClientRect() 与 .getClientRects()的关系

  1. 这两个方法的区别与当前的 display 相关, 当 display=inline 时, .getClientRects() 返回当前节点内每一行文本的 ClientRect 对象数组, 此时数组长度等于文本行数。
  2. 当 display != inline 时, .getClientRects() 返回当前节点的 ClientRect 对象数组,此时数组长度为1.
  3. .getBoundingClientRect() 总是返回当前节点的 ClientRect 对象, 注意这里是 ClientRect 对象而不是对象数组。

提示

以上测试, 可以通过在百度首页执行进行测试, document.querySelect('#s_kw_wrap') 所获取到的节点为百度首页输入框

希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
该如何加载google-analytics(或其他第三方)的JS
May 13 Javascript
初窥JQuery(二) 事件机制(1)
Nov 25 Javascript
jQuery控制的不同方向的滑动(向左、向右滑动等)
Jul 18 Javascript
javacript使用break内层跳出外层循环分析
Jan 12 Javascript
关于Javascript回调函数的一个妙用
Aug 29 Javascript
Javascript中的async awai的用法
May 17 Javascript
jQueryeasyui 中如何使用datetimebox 取两个日期间相隔的天数
Jun 13 jQuery
浅谈ES6 模板字符串的具体使用方法
Nov 07 Javascript
简述vue中的config配置
Jan 23 Javascript
Vue自定义全局Toast和Loading的实例详解
Apr 18 Javascript
Postman环境变量全局变量使用方法详解
Aug 13 Javascript
electron踩坑之remote of undefined的解决
Oct 06 Javascript
微信小程序 监听手势滑动切换页面实例详解
Jun 15 #Javascript
微信小程序canvas写字板效果及实例
Jun 15 #Javascript
AngularJs实现聊天列表实时刷新功能
Jun 15 #Javascript
bootstrap daterangepicker双日历时间段选择控件详解
Jun 15 #Javascript
详解react-router如何实现按需加载
Jun 15 #Javascript
jQuery实现 RadioButton做必选校验功能
Jun 15 #jQuery
bootstrap daterangepicker汉化以及扩展功能
Jun 15 #Javascript
You might like
PHP合并discuz用户脚本的方法
2015/08/04 PHP
在Laravel5中正确设置文件权限的方法
2019/05/22 PHP
把JS与CSS写在同一个文件里的书写方法
2007/06/02 Javascript
网络图片延迟加载实现代码 超越jquery控件
2010/03/27 Javascript
在一个js文件里远程调用jquery.js会在ie8下的一个奇怪问题
2010/11/28 Javascript
jquery防止重复执行动画避免页面混乱
2014/04/22 Javascript
一款由jquery实现的整屏切换特效
2014/09/15 Javascript
Javascript字符串浏览器兼容问题分析
2014/12/01 Javascript
window.location.reload 刷新使用分析(去对话框)
2015/11/11 Javascript
JavaScript中误用/g导致的正则test()无法正确重复执行的解决方案
2016/07/27 Javascript
jQuery中的deferred使用方法
2017/03/27 jQuery
jQuery.Form实现Ajax上传文件同时设置headers的方法
2017/06/26 jQuery
vue2项目使用sass的示例代码
2017/06/28 Javascript
深入研究jQuery图片懒加载 lazyload.js使用方法
2017/08/16 jQuery
nodejs创建简易web服务器与文件读写的实例
2017/09/07 NodeJs
webpack4+Vue搭建自己的Vue-cli项目过程分享
2018/08/29 Javascript
用Vue编写抽象组件的方法
2019/05/06 Javascript
解决vue组件props传值对象获取不到的问题
2019/06/06 Javascript
JavaScript适配器模式原理与用法实例详解
2020/03/09 Javascript
ant design的table组件实现全选功能以及自定义分页
2020/11/17 Javascript
python3图片转换二进制存入mysql
2013/12/06 Python
Python实现将HTML转换成doc格式文件的方法示例
2017/11/20 Python
Python+tkinter使用40行代码实现计算器功能
2018/01/30 Python
Python List cmp()知识点总结
2019/02/18 Python
Python爬虫爬取新闻资讯案例详解
2020/07/14 Python
美国最受欢迎的度假目的地优惠套餐:BookVIP
2018/09/27 全球购物
结婚喜宴家长答谢词
2014/01/15 职场文书
电子商务专业求职信
2014/03/08 职场文书
诚信考试倡议书
2014/04/15 职场文书
大学开学计划书
2014/04/30 职场文书
关于美容院的活动方案
2014/08/14 职场文书
个人公司授权委托书范本
2014/10/12 职场文书
三潭印月的导游词
2015/02/12 职场文书
2019年汽车租赁合同范本!
2019/08/12 职场文书
导游词之太行山青龙峡
2020/01/14 职场文书
Android使用EventBus发送消息,Fragment中接收消息的方法会执行多次
2022/04/24 Java/Android