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 相关文章推荐
Gird组件 Part-3:范例RSSFeed Viewer
Mar 10 Javascript
Javascript类定义语法,私有成员、受保护成员、静态成员等介绍
Dec 08 Javascript
JavaSript中变量的作用域闭包的深入理解
May 12 Javascript
Jquery 实现table样式的设定
Jan 28 Javascript
JavaScript实现常用二级省市级联下拉列表的方法
Mar 25 Javascript
javascript实现在网页任意处点左键弹出隐藏菜单的方法
May 13 Javascript
使用RN Animated做一个“添加购物车”动画的方法
Sep 12 Javascript
详解微信小程序的不同函数调用的几种方法
May 08 Javascript
JavaScript获取某一天所在的星期
Sep 05 Javascript
es6中reduce的基本使用方法
Sep 10 Javascript
ES6 Generator基本使用方法示例
Jun 06 Javascript
vue data对象重新赋值无效(未更改)的解决方式
Jul 24 Javascript
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
IIS6的PHP最佳配置方法
2007/03/19 PHP
php删除文件夹及其文件夹下所有文件的函数代码
2013/01/23 PHP
解析php中var_dump,var_export,print_r三个函数的区别
2013/06/21 PHP
session 加入redis的实现代码
2016/07/15 PHP
PHP中in_array函数使用的问题与解决办法
2016/09/11 PHP
PHP实现上传图片到 zimg 服务器
2016/10/19 PHP
thinkphp5.1框架容器与依赖注入实例分析
2019/07/23 PHP
javascript闭包的理解和实例
2010/08/12 Javascript
仿新浪微博返回顶部的jquery实现代码
2012/10/01 Javascript
javascript获取重复次数最多的字符
2015/07/08 Javascript
高效的jquery数字滚动特效
2015/12/17 Javascript
jQuery实现多级联动下拉列表查询框
2016/01/18 Javascript
全国省市二级联动下拉菜单 js版
2016/05/10 Javascript
利用JS实现页面删除并重新排序功能
2016/12/09 Javascript
nodejs实现发出蜂鸣声音(系统报警声)的方法
2017/01/18 NodeJs
Vue.js实现的表格增加删除demo示例
2018/05/22 Javascript
vue 父组件中调用子组件函数的方法
2019/06/06 Javascript
使用uni-app开发微信小程序的实现
2019/12/13 Javascript
Vue 实例中使用$refs的注意事项
2021/01/29 Vue.js
星球大战与Python之间的那些事
2016/01/07 Python
Python中operator模块的操作符使用示例总结
2016/06/28 Python
python 中文件输入输出及os模块对文件系统的操作方法
2018/08/27 Python
python2.7的flask框架之引用js&css等静态文件的实现方法
2019/08/22 Python
python绘制BA无标度网络示例代码
2019/11/21 Python
Python实现自动打开电脑应用的示例代码
2020/04/17 Python
使用Pycharm(Python工具)新建项目及创建Python文件的教程
2020/04/26 Python
高三自我鉴定
2013/10/23 职场文书
市场总经理岗位职责
2014/04/11 职场文书
食品安全工作方案
2014/05/07 职场文书
团干部培训方案
2014/06/03 职场文书
2014年班级工作总结
2014/11/14 职场文书
2014年外联部工作总结
2014/11/17 职场文书
社区敬老月活动总结
2015/05/07 职场文书
单位领导婚礼致辞
2015/07/28 职场文书
MySQL命令行操作时的编码问题详解
2021/04/14 MySQL
上帝为你开了一扇窗之Tkinter常用函数详解
2021/06/02 Python