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 相关文章推荐
关于IE7 IE8弹出窗口顶上
Dec 22 Javascript
js获取html页面节点方法(递归方式)
Dec 13 Javascript
JS获取select的value和text值的简单实例
Feb 26 Javascript
jQuery Validate初步体验(二)
Dec 12 Javascript
AngualrJS中每次$http请求时的一个遮罩层Directive
Jan 26 Javascript
Angular中的$watch、$watchGroup、$watchCollection
Jun 25 Javascript
在vue中封装可复用的组件方法
Mar 01 Javascript
vue配置请求本地json数据的方法
Apr 11 Javascript
vue学习笔记五:在vue项目里面使用引入公共方法详解
Apr 04 Javascript
JS实现根据详细地址获取经纬度功能示例
Apr 16 Javascript
vue2.0+SVG实现音乐播放圆形进度条组件
Sep 21 Javascript
javascript操作元素的常见方法小结
Nov 13 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
推荐几款用 Sublime Text 开发 Laravel 所用到的插件
2014/10/30 PHP
PHP中TP5 上传文件的实例详解
2017/07/31 PHP
基于Laravel-admin 后台的自定义页面用法详解
2019/09/30 PHP
PHP常用header头定义代码示例汇总
2020/08/29 PHP
cookie在javascript中的使用技巧以及隐私在服务器端的设置
2012/12/03 Javascript
js 在定义的时候立即执行的函数表达式(function)写法
2013/01/16 Javascript
js setTimeout()函数介绍及应用以倒计时为例
2013/12/12 Javascript
JS中产生20位随机数以0-9为例也可以是a-z A-Z
2014/08/01 Javascript
纯javascript实现的小游戏《Flappy Pig》实例
2015/07/27 Javascript
jQuery焦点图轮播特效代码分享(3款)
2015/09/05 Javascript
JS中dom0级事件和dom2级事件的区别介绍
2016/05/05 Javascript
Javascript中Promise的四种常用方法总结
2017/07/14 Javascript
laydate日历控件使用方法详解
2017/11/20 Javascript
Vue 2.5.2下axios + express 本地请求404的解决方法
2018/02/21 Javascript
Vue 通过自定义指令回顾v-内置指令(小结)
2018/09/03 Javascript
js指定日期增加指定月份的实现方法
2018/12/19 Javascript
javascript网页随机点名实现过程解析
2019/10/15 Javascript
微信小程序实现翻牌抽奖动画
2020/09/21 Javascript
解决antd Form 表单校验方法无响应的问题
2020/10/27 Javascript
vue3.0实现插件封装
2020/12/14 Vue.js
python类装饰器用法实例
2015/06/04 Python
Python实现二叉搜索树
2016/02/03 Python
简单实现Python爬取网络图片
2018/04/01 Python
Python爬虫PyQuery库基本用法入门教程
2018/08/04 Python
浅谈pandas用groupby后对层级索引levels的处理方法
2018/11/06 Python
Python3远程监控程序的实现方法
2019/07/15 Python
python多进程使用函数封装实例
2020/05/02 Python
关于matplotlib-legend 位置属性 loc 使用说明
2020/05/16 Python
一组SQL面试题
2016/02/15 面试题
物流管理专业大学生自荐信
2013/10/04 职场文书
2014年庆元旦活动方案
2014/02/15 职场文书
安踏广告词改编版
2014/03/21 职场文书
2014年入党积极分子党校培训心得体会
2014/07/08 职场文书
党员四风自我剖析材料
2014/10/07 职场文书
庆元旦主持词
2015/07/06 职场文书
话题作文之财富(600字)
2019/12/03 职场文书