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 相关文章推荐
javascript代码编写需要注意的7个小细节小结
Sep 21 Javascript
用RadioButten或CheckBox实现div的显示与隐藏
Sep 21 Javascript
解决js中window.open弹出的是上次的缓存页面问题
Dec 29 Javascript
jQuery 选择同时包含两个class的元素的实现方法
Jun 01 Javascript
JS 日期与时间戮相互转化的简单实例
Jun 22 Javascript
微信小程序前端源码逻辑和工作流
Sep 25 Javascript
jQuery实现页面倒计时并刷新效果
Mar 13 Javascript
利用 spin.js 生成等待效果(js 等待效果)
Jun 25 Javascript
JavaScript面向对象的程序设计(犯迷糊的小羊)
May 27 Javascript
react-router 路由切换动画的实现示例
Dec 03 Javascript
Node.js原生api搭建web服务器的方法步骤
Feb 15 Javascript
JS三级联动代码格式实例详解
Dec 30 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
PHP检测移动设备类mobile detection使用实例
2014/04/14 PHP
Codeigniter实现处理用户登录验证后的URL跳转
2014/06/12 PHP
php实现的九九乘法口诀表简洁版
2014/07/28 PHP
Yii2增加验证码步骤详解
2016/04/25 PHP
ThinkPHP 框架实现的读取excel导入数据库操作示例
2020/04/14 PHP
javascript编程起步(第四课)
2007/02/27 Javascript
JavaScript游戏之优化篇
2010/11/08 Javascript
基于json的jquery地区联动效果代码
2011/07/06 Javascript
由JavaScript中call()方法引发的对面向对象继承机制call的思考
2011/09/12 Javascript
jQuery-Easyui 1.2 实现多层菜单效果的代码
2012/01/13 Javascript
Javascript 多浏览器兼容总结(实战经验)
2013/10/30 Javascript
iframe的onreadystatechange事件在firefox下的使用
2014/04/16 Javascript
Ext JS框架程序中阻止键盘触发回退或者刷新页面的代码分享
2016/06/07 Javascript
Bootstrap学习笔记之js组件(4)
2016/06/12 Javascript
canvas实现十二星座星空图
2017/02/14 Javascript
Dropify.js图片宽高自适应的方法
2017/11/27 Javascript
jQuery实现左右滑动的toggle方法
2018/03/03 jQuery
微信小程序input框中加入小图标的实现方法
2018/06/19 Javascript
微信小程序实现判断是分享到群还是个人功能示例
2019/05/03 Javascript
python抓取网页图片示例(python爬虫)
2014/04/27 Python
python anaconda 安装 环境变量 升级 以及特殊库安装的方法
2017/06/21 Python
TensorFlow实现AutoEncoder自编码器
2018/03/09 Python
关于python写入文件自动换行的问题
2018/06/23 Python
Python定义一个跨越多行的字符串的多种方法小结
2018/07/19 Python
通过PYTHON来实现图像分割详解
2019/06/26 Python
OpenCV 轮廓检测的实现方法
2019/07/03 Python
基于Tensorflow使用CPU而不用GPU问题的解决
2020/02/07 Python
python 实现在无序数组中找到中位数方法
2020/03/03 Python
利用Node实现HTML5离线存储的方法
2020/10/16 HTML / CSS
菲律宾最大的网上花店和礼品店:PhilFlower.com
2018/02/09 全球购物
车间操作工岗位职责
2013/12/19 职场文书
教室布置标语
2014/06/26 职场文书
详解Redis复制原理
2021/06/04 Redis
十大最强岩石系宝可梦,怪颚龙实力最强,第七破坏力很强
2022/03/18 日漫
Zabbix对Kafka topic积压数据监控的解决方案
2022/07/07 Servers
MySQL下载安装配置详细教程 附下载资源
2022/09/23 MySQL