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 相关文章推荐
jquery快捷动态绑定键盘事件的操作函数代码
Oct 17 Javascript
jQuery获得子元素个数的方法
Apr 14 Javascript
JavaScript获取两个数组交集的方法
Jun 09 Javascript
Javascript编写2048小游戏
Jul 07 Javascript
浅谈JavaScript前端开发的MVC结构与MVVM结构
Jun 03 Javascript
Vue.js实现在下拉列表区域外点击即可关闭下拉列表的功能(自定义下拉列表)
May 30 Javascript
JavaScript数组去重的多种方法(四种)
Sep 19 Javascript
JavaScript实现JSON合并操作示例【递归深度合并】
Sep 07 Javascript
Vue 组件修改根实例的数据的方法
Apr 02 Javascript
vue transition 在子组件中失效的解决
Nov 12 Javascript
js实现打字小游戏
Dec 17 Javascript
适用于 Vue 的播放器组件Vue-Video-Player操作
Nov 16 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中的数组处理函数实例总结
2016/01/09 PHP
Zend Framework教程之模型Model用法简单实例
2016/03/04 PHP
查看大图功能代码jquery版
2013/11/05 Javascript
页面实时更新时间的JS实例代码
2013/12/18 Javascript
javascript异步编程的4种方法
2014/02/19 Javascript
js获取数组的最后一个元素
2015/04/14 Javascript
TypeScript Type Innference(类型判断)
2016/03/10 Javascript
jQuery实现圣诞节礼物动画案例解析
2016/12/25 Javascript
Node.js中sequelize时区的配置方法
2017/12/10 Javascript
Vue render深入开发讲解
2018/04/13 Javascript
浅析vue 函数配置项watch及函数 $watch 源码分享
2018/11/22 Javascript
示例vue 的keep-alive缓存功能的实现
2018/12/13 Javascript
JavaScript解析及序列化JSON的方法实例分析
2019/01/04 Javascript
详解Webpack如何引入CDN链接来优化编译后的体积
2019/06/21 Javascript
详解Vue中CSS样式穿透问题
2019/09/12 Javascript
electron踩坑之remote of undefined的解决
2020/10/06 Javascript
在Linux下使用Python的matplotlib绘制数据图的教程
2015/06/11 Python
Python增量循环删除MySQL表数据的方法
2016/09/23 Python
Python视频爬虫实现下载头条视频功能示例
2018/05/07 Python
详解Python_shutil模块
2019/03/15 Python
tensorflow实现在函数中用tf.Print输出中间值
2020/01/21 Python
Python 转移文件至云对象存储的方法
2021/02/07 Python
css3 给背景设置渐变色的方法
2019/09/12 HTML / CSS
加拿大快时尚零售商:Ardene
2018/02/14 全球购物
Omio葡萄牙:全欧洲低价大巴、火车和航班搜索和比价
2019/02/09 全球购物
面向对象概念面试题(.NET)
2016/11/04 面试题
百度吧主申请感言
2014/01/12 职场文书
产品促销活动策划书
2014/01/15 职场文书
回门宴父母答谢词
2014/01/26 职场文书
效能监察建议书
2014/05/19 职场文书
房屋租赁合同补充协议
2014/10/11 职场文书
四风问题专项整治工作情况报告
2014/10/28 职场文书
让生命充满爱观后感
2015/06/08 职场文书
深度好文:50条没人告诉你的人生经验,句句精辟
2019/08/22 职场文书
python入门之算法学习
2021/04/22 Python
我国拿下天问一号火星着陆区附近 22 个地理实体命名:平乐、西柏坡、古田、漠河等
2022/04/29 数码科技