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 While 循环基础教程
Apr 05 Javascript
jquery showModelDialog的使用方法示例详解
Nov 19 Javascript
jquery-syntax动态语法着色示例代码
May 14 Javascript
node.js中的fs.symlink方法使用说明
Dec 15 Javascript
60行js代码实现俄罗斯方块
Mar 31 Javascript
JAVASCRIPT代码编写俄罗斯方块网页版
Nov 26 Javascript
基于JS实现移动端访问PC端页面时跳转到对应的移动端网页
Dec 24 Javascript
微信+angularJS的SPA应用中用router进行页面跳转,jssdk校验失败问题解决
Sep 09 Javascript
Bootstrapvalidator校验、校验清除重置的实现代码(推荐)
Sep 28 Javascript
JQuery.validationEngine表单验证插件(推荐)
Dec 10 Javascript
如何解决jQuery 和其他JS库的冲突
Jun 22 jQuery
vue项目开启Gzip压缩和性能优化操作
Oct 26 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/10/09 PHP
php json转换相关知识(小结)
2018/12/21 PHP
javascript英文日期(有时间)选择器
2007/05/02 Javascript
各浏览器对click方法的支持差异小结
2011/07/31 Javascript
NodeJS的url截取模块url-extract的使用实例
2013/11/18 NodeJs
AngularJS入门教程(二):AngularJS模板
2014/12/06 Javascript
用window.onerror捕获并上报Js错误的方法
2016/01/27 Javascript
jQuery ajax请求返回list数据动态生成input标签,并把list数据赋值到input标签
2016/03/29 Javascript
JavaScript判断用户名和密码不能为空的实现代码
2016/05/16 Javascript
基于Bootstrap里面的Button dropdown打造自定义select
2016/05/30 Javascript
Bootstrap风格的WPF样式
2016/12/07 Javascript
JavaScript定时器setTimeout()和setInterval()详解
2017/08/18 Javascript
原生JS实现前端本地文件上传
2018/09/08 Javascript
vue-cli 目录结构详细讲解总结
2019/01/15 Javascript
用Electron写个带界面的nodejs爬虫的实现方法
2019/01/29 NodeJs
微信小程序封装自定义弹窗的实现代码
2019/05/08 Javascript
在vue中利用v-html按分号将文本换行的例子
2019/11/14 Javascript
js实现幻灯片轮播图
2020/08/14 Javascript
JS绘图Flot应用图形绘制异常解决方案
2020/10/16 Javascript
[37:35]DOTA2上海特级锦标赛A组资格赛#1 Secret VS MVP.Phx第二局
2016/02/25 DOTA
[01:51]历届DOTA2国际邀请赛举办地回顾 TI9落地上海
2018/08/26 DOTA
基于Python实现文件大小输出
2016/01/11 Python
Pandas之drop_duplicates:去除重复项方法
2018/04/18 Python
python3的print()函数的用法图文讲解
2019/07/16 Python
Python进程Multiprocessing模块原理解析
2020/02/28 Python
Ibatis中如何提高SQL Map的性能
2013/05/11 面试题
淘宝客服专员岗位职责
2014/04/11 职场文书
娱乐节目策划方案
2014/06/10 职场文书
意外事故赔偿协议书
2016/03/22 职场文书
导游词之南京中山陵
2019/11/27 职场文书
《悬崖边的树》读后感2篇
2019/12/02 职场文书
测量JavaScript函数的性能各种方式对比
2021/04/27 Javascript
golang 如何用反射reflect操作结构体
2021/04/28 Golang
go goroutine 怎样进行错误处理
2021/07/16 Golang
Golang数据类型和相互转换
2022/04/12 Golang
Nginx如何配置多个服务域名解析共用80端口详解
2022/09/23 Servers