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 相关文章推荐
WEB高性能开发之疯狂的HTML压缩
Jun 19 Javascript
jQuery帮助之筛选查找 children([expr])
Jan 31 Javascript
用jquery和json从后台获得数据集的代码
Nov 07 Javascript
Jquery插件之打造自定义的select标签
Nov 30 Javascript
IE、FF浏览器下修改标签透明度
Jan 28 Javascript
js文件包含的几种方式介绍
Sep 28 Javascript
node.js中的fs.fchownSync方法使用说明
Dec 16 Javascript
原生JS和JQuery动态添加、删除表格行的方法
May 28 Javascript
vue.js的安装方法
May 12 Javascript
ES6 javascript的异步操作实例详解
Oct 30 Javascript
详解webpack + react + react-router 如何实现懒加载
Nov 20 Javascript
element-ui table组件如何使用render属性的实现
Nov 04 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模板,主要想体现一下思路
2006/12/25 PHP
PHP5中新增stdClass 内部保留类
2011/06/13 PHP
php面向对象 字段的声明与使用
2012/06/14 PHP
php微信开发之批量生成带参数的二维码
2016/06/26 PHP
php面向对象基础详解【星际争霸游戏案例】
2020/01/23 PHP
JQuery自定义事件的应用 JQuery最佳实践
2010/08/01 Javascript
为超链接加上disabled后的故事
2010/12/10 Javascript
HTML页面滚动时获取离页面顶部的距离2种实现方法
2013/09/05 Javascript
JS定时器实例详细分析
2013/10/11 Javascript
对于Form表单reset方法的新认识
2014/03/05 Javascript
分享纯手写漂亮的表单验证
2015/11/19 Javascript
jQuery配合coin-slider插件制作幻灯片效果的流程解析
2016/05/13 Javascript
手把手教你vue-cli单页到多页应用的方法
2018/05/31 Javascript
Jquery实现无缝向上循环滚动列表的特效
2019/02/13 jQuery
关于angular引入ng-zorro的问题浅析
2020/09/09 Javascript
JS代码实现页面切换效果
2021/01/10 Javascript
[01:05:40]2014 DOTA2国际邀请赛中国区预选赛 5 23 CIS VS DT第三场
2014/05/24 DOTA
Python NumPy库安装使用笔记
2015/05/18 Python
Python元组操作实例分析【创建、赋值、更新、删除等】
2017/07/24 Python
Windows环境下python环境安装使用图文教程
2018/03/13 Python
python的concat等多种用法详解
2018/11/28 Python
Python银行系统实战源码
2019/10/25 Python
解决Python二维数组赋值问题
2019/11/28 Python
浅析Django中关于session的使用
2019/12/30 Python
tensorflow 变长序列存储实例
2020/01/20 Python
python程序需要编译吗
2020/06/19 Python
Python爬虫模拟登陆哔哩哔哩(bilibili)并突破点选验证码功能
2020/12/21 Python
健康监测猫砂:Pretty Litter
2017/05/25 全球购物
Belvilla法国:休闲度假房屋出租
2020/10/03 全球购物
临床医学专业学生的自我评价分享
2013/11/21 职场文书
网络程序员自荐信
2014/01/25 职场文书
小学先进集体事迹材料
2014/05/31 职场文书
幼儿生日活动方案
2014/08/27 职场文书
房屋分割离婚协议书范本
2014/12/01 职场文书
健康教育主题班会
2015/08/14 职场文书
CSS 实现多彩、智能的阴影效果
2021/05/12 HTML / CSS