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 相关文章推荐
zeroclipboard复制到剪切板的flash
Aug 04 Javascript
js 自动播放的实例代码
Nov 19 Javascript
Js实现当前点击a标签变色突出显示其他a标签回复原色
Nov 27 Javascript
jquery和javascript中如何将一元素的内容赋给另一元素
Jan 09 Javascript
基于ajax和jsonp的原生封装(实例)
Oct 16 Javascript
利用jQuery实现简单的拖曳效果实例代码
Oct 20 jQuery
Vue2.0点击切换类名改变样式的方法
Aug 22 Javascript
WebGL学习教程之Three.js学习笔记(第一篇)
Apr 25 Javascript
小程序实现搜索界面 小程序实现推荐搜索列表效果
May 18 Javascript
如何提升vue.js中大型数据的性能
Jun 21 Javascript
Vue 实现一个命令式弹窗组件功能
Sep 25 Javascript
Vue.js 中制作自定义选择组件的代码附演示demo
Feb 28 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实现的sqlite数据库连接类
2014/12/12 PHP
php单例模式的简单实现方法
2016/06/10 PHP
用jQuery技术实现Tab页界面之二
2009/09/21 Javascript
javascript定义函数的方法
2010/12/06 Javascript
jQuery源码分析-04 选择器-Sizzle-工作原理分析
2011/11/14 Javascript
jQuery的Ajax的自动完成功能控件简要说明
2013/02/22 Javascript
js函数名与form表单元素同名冲突的问题
2014/03/07 Javascript
Area 区域实现post提交数据的js写法
2014/04/22 Javascript
使用JS画图之点、线、面
2015/01/12 Javascript
JavaScript父子窗体间的调用方法
2015/03/31 Javascript
使用canvas实现仿新浪微博头像截取上传功能
2015/09/02 Javascript
JS实现选项卡实例详解
2015/11/17 Javascript
jquery+ajax+text文本框实现智能提示完整实例
2016/07/09 Javascript
利用JS实现文字的聚合动画效果
2017/01/22 Javascript
用js制作淘宝放大镜效果
2020/10/28 Javascript
javascript 数据存储的常用函数总结
2017/06/01 Javascript
vue2.0 父组件给子组件传递数据的方法
2018/01/15 Javascript
vue 导航菜单刷新状态不消失,显示对应的路由界面操作
2020/08/06 Javascript
vue-cli3中配置alias和打包加hash值操作
2020/09/04 Javascript
vue项目中微信登录的实现操作
2020/09/08 Javascript
vue动态合并单元格并添加小计合计功能示例
2020/11/26 Vue.js
[01:08:17]2018DOTA2亚洲邀请赛3月29日 小组赛B组 EG VS VGJ.T
2018/03/30 DOTA
Python运行报错UnicodeDecodeError的解决方法
2016/06/07 Python
[原创]Python入门教程4. 元组基本操作
2018/10/31 Python
python中ImageTk.PhotoImage()不显示图片却不报错问题解决
2018/12/06 Python
python实现二维数组的对角线遍历
2019/03/02 Python
django rest framework使用django-filter用法
2020/07/15 Python
Python 利用OpenCV给照片换底色的示例代码
2020/08/03 Python
Python内置函数及功能简介汇总
2020/10/13 Python
HTML5 canvas基本绘图之图形组合
2016/06/27 HTML / CSS
linux面试题参考答案(6)
2016/06/23 面试题
自我评价优秀范文分享
2013/11/30 职场文书
国培远程培训感言
2014/03/08 职场文书
第二课堂活动总结
2014/05/07 职场文书
理想国读书笔记
2015/06/25 职场文书
SpringDataJPA实体类关系映射配置方式
2021/12/06 Java/Android