JavaScript中计算网页中某个元素的位置


Posted in Javascript onJune 10, 2015

由于项目的需要,测试中需要对网页元素进行截图,以确保它看上去没有问题。之前我写过一篇文章介绍过一种方法,先使用 WebDriver 进行全屏截图,然后根据目标元素(DOM Element)所在的位置,再对截下来的图片进行剪裁,保留我们需要的位置即可。

那段代码一直都工作得很好,直到我知道了一个东西:iframe。iframe(普通的 frame 也是一样的,不过 frame 现在不太常见,这里只用 iframe 举例)中的内容被视为一个独立的网页,连 Window 对象也是和它的父级网页分开的。而 WebDriver 中的 WebElement.getLocation()方法只能返回这个 WebElement 和它所在的 Window 的位置关系,它的实现没什么问题,但全屏截图不仅包含了 iframe 的内容,可能也包含了它的父级页面的内容,剪裁的时候需要知道目标元素在截图中的位置。那么问题来了,挖掘机技术哪家强?如何计算一个元素相对于截图的位置?

这个问题还要分类讨论,原因是:Chrome 和 Firefox 中截图的行为是不一样的。Chrome 的截图是当前可见(viewport)的网页内容,比方说,当网页的实际大小超过 Chrome 窗口大小时,根据滚动条的位置不同,窗口中显示的内容不同,Chrome 的截图就是显示出来的内容。于是我们要计算目标元素相对于当前可见内容的位置。而 Firefox 用了一个方法,可以截到整个网页的内容,无视当前窗口大小。于是对于 Firefox 我们要计算元素的绝对位置(Absolute Position)。

获得一个元素的位置,需要用到一个方法:Element.getBoundingClientRect()。这个方法返回这个元素相对于它所处的 Windows 在当前可见内容的位置,用 top、left、right、bottom 四个值来表示。我们只关心其中的 top 和 left,至于剪裁的尺寸,我们可以通过元素本身的长和宽来得到,不需要计算。要计算目标元素对于顶级 Window的位置,我们只需要依次加上它的父级 Window的 top 和 left 即可。代码如下:

function calcViewportLocation(element) {
 var currentWindow = window;
 var rect = element.getBoundingClientRect(); // 元素的位置
 var top = rect.top;
 var left = rect.left;
 while (currentWindow.frameElement != null) { // 处理父级 Window
  element = currentWindow.frameElement;
  currentWindow = currentWindow.parent;
  rect = element.getBoundingClientRect();
  if (rect.top > 0) { top += rect.top; }
  if (rect.left > 0) { left += rect.left; }
 }
 return [Math.round(top), Math.round(left)];
}

以上代码适用于 Chrome ,而在 Firefox 中,我们还需要计算元素的绝对位置。这里需要用到 Window.pageXOffset。pageXOffset,或者 scrollX,表示当前 Window 的横向滚动条滚动的位置,把这个值和上述的 left 相加,即可得到目标元素的横向绝对位置。当然,iframe 也可以特殊处理的:

function calcAbsolutLocation(element) {
 var top = 0;
 var left = 0;
 var currentWindow = window;
 while (element != null) {
  rect = element.getBoundingClientRect();
  var pageYOffset = currentWindow.pageYOffset;
  var pageXOffset = currentWindow.pageXOffset;
  if (typeof pageYOffset === 'undefined') { // IE8
   currentDocument = currentWindow.document;
   var bodyElement = (currentDocument.documentElement
     || currentDocument.body.parentNode || currentDocument.body);
   pageYOffset = bodyElement.scrollTop;
   pageXOffset = bodyElement.scrollLeft;
  }
  top += rect.top + pageYOffset;
  left += rect.left + pageXOffset;
  element = currentWindow.frameElement;
  currentWindow = currentWindow.parent;
  if (element != null) {
   style = window.getComputedStyle(element);
   top += parseInt(style.borderTopWidth, 10);
   left += parseInt(style.borderLeftWidth, 10);
  }
 }
 return [Math.round(top), Math.round(left)];
}

由于 IE8 不支持 pageXOffset 和 scrollX,于是在 IE8 中需要一些特殊处理,即代码中标注“IE8”的部分。把这两段 Javascript 代码,替换之前文中的 WebElement.getLocation(),即可实现在 iframe 中对特定元素截图。

Javascript 相关文章推荐
根据分辨率不同,调用不同的css文件
Aug 25 Javascript
document.onreadystatechange事件的用法分析
Oct 17 Javascript
jquery插件之easing使用
Aug 19 Javascript
处理及遍历XML文档DOM元素属性及方法整理
Aug 23 Javascript
原生javaScript实现图片延时加载的方法
Dec 22 Javascript
jQuery寻找n以内完全数的方法
Jun 24 Javascript
jQuery UI制作选项卡(tabs)
Dec 13 Javascript
JavaScript原生数组Array常用方法
Apr 06 Javascript
VUE axios发送跨域请求需要注意的问题
Jul 06 Javascript
H5实现仿flash效果的实现代码
Sep 29 Javascript
浅析微信小程序modal弹窗关闭默认会执行cancel问题
Oct 14 Javascript
vue中this.$http.post()跨域和请求参数丢失的解决
Apr 08 Vue.js
JavaScript实现强制重定向至HTTPS页面
Jun 10 #Javascript
详解JavaScript中getFullYear()方法的使用
Jun 10 #Javascript
JavaScript中判断函数、变量是否存在
Jun 10 #Javascript
Javascript中实现String.startsWith和endsWith方法
Jun 10 #Javascript
Javascript中判断对象是否为空
Jun 10 #Javascript
javascript事件委托的方式绑定详解
Jun 10 #Javascript
个人总结的一些JavaScript技巧、实用函数、简洁方法、编程细节
Jun 10 #Javascript
You might like
解析php框架codeigniter中如何使用框架的session
2013/06/24 PHP
CI框架中cookie的操作方法分析
2014/12/12 PHP
php使用PDO事务配合表格读取大量数据插入操作实现方法
2017/02/16 PHP
php中文乱码问题的终极解决方案汇总
2017/08/01 PHP
关于onScroll事件在IE6下每次滚动触发三次bug说明
2011/09/21 Javascript
JS Replace 全部替换字符的用法小结
2013/12/24 Javascript
js跨域问题浅析及解决方法优缺点对比
2014/11/08 Javascript
JavaScript实现定时隐藏与显示图片的方法
2015/08/06 Javascript
如何利用JSHint减少JavaScript的错误
2016/08/23 Javascript
JavaScript组合模式学习要点
2016/08/26 Javascript
原生js实现tab选项卡切换
2020/03/23 Javascript
浅谈angular.js跨域post解决方案
2017/08/30 Javascript
代码整洁之道(重构)
2018/10/25 Javascript
微信小程序保存多张图片的实现方法
2019/03/05 Javascript
使用异步组件优化Vue应用程序的性能
2019/04/28 Javascript
微信小程序开发之左右分栏效果的实例代码
2019/05/20 Javascript
vue下使用nginx刷新页面404的问题解决
2019/08/02 Javascript
vue限制输入框只能输入8位整数和2位小数的代码
2019/11/06 Javascript
Python利用pyHook实现监听用户鼠标与键盘事件
2014/08/21 Python
Python实现变量数值交换及判断数组是否含有某个元素的方法
2017/09/18 Python
Python使用Pandas读写Excel实例解析
2019/11/19 Python
Python基于requests库爬取网站信息
2020/03/02 Python
python属于解释型语言么
2020/06/15 Python
HTML5基于flash实现播放RTMP协议视频的示例代码
2020/12/04 HTML / CSS
工商管理专业大学生职业生涯规划范文
2014/03/09 职场文书
家长对老师的感言
2014/03/11 职场文书
道德大讲堂实施方案
2014/05/14 职场文书
2014年最新大专生职业生涯规划书范文
2014/09/13 职场文书
离婚律师函范本
2015/05/27 职场文书
工商局调档介绍信
2015/10/22 职场文书
个人职业生涯规划之自我评估篇
2019/09/03 职场文书
解决python3安装pandas出错的问题
2021/05/20 Python
Python3中最常用的5种线程锁实例总结
2021/07/07 Python
Canvas绘制像素风图片的示例代码
2021/09/25 HTML / CSS
Python OpenCV之常用滤波器使用详解
2022/04/07 Python
MySQL的prepare使用以及遇到的bug
2022/05/11 MySQL