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父窗口控制只弹出一个子窗口
Apr 10 Javascript
JavaScript 获得选中文本内容的方法
Feb 15 Javascript
jQuery 使用手册(四)
Sep 23 Javascript
jquery 操作日期、星期、元素的追加的实现代码
Feb 07 Javascript
javascript中this做事件参数相关问题解答
Mar 17 Javascript
jquery的live使用注意事项
Feb 18 Javascript
使用AngularJS实现可伸缩的页面切换的方法
Jun 19 Javascript
基于jQuery实现动态搜索显示功能
May 05 Javascript
ES6解构赋值实例详解
Oct 31 Javascript
jQuery实现右侧抽屉式在线客服功能
Dec 25 jQuery
最简单的vue消息提示全局组件的方法
Jun 16 Javascript
微信jssdk踩坑之签名错误invalid signature
May 19 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
《魔兽争霸3》重制版究竟重制了什么?玩家:这么糊弄真的好吗?
2020/05/04 魔兽争霸
CodeIgniter错误mysql_connect(): No such file or directory解决方法
2014/09/06 PHP
关于扩展 Laravel 默认 Session 中间件导致的 Session 写入失效问题分析
2016/01/08 PHP
Linux下源码包安装Swoole及基本使用操作图文详解
2019/04/02 PHP
PHP Swoole异步MySQL客户端实现方法示例
2019/10/24 PHP
php框架CI(codeigniter)自动加载与自主创建对象操作实例分析
2020/06/06 PHP
jquery jqPlot API 中文使用教程(非常强大的图表工具)
2011/08/15 Javascript
js点击事件链接的问题解决
2014/04/25 Javascript
jquery 选取方法都有哪些
2014/05/18 Javascript
input:checkbox多选框实现单选效果跟radio一样
2014/06/16 Javascript
仿淘宝TAB切换搜索框搜索切换的相关内容
2014/09/21 Javascript
js实现多选项切换导航菜单的方法
2015/02/06 Javascript
浅谈NodeJS中require路径问题
2015/05/07 NodeJs
Jquery和Js获得元素标签名称的方法总结
2016/10/08 Javascript
nodejs 终端打印进度条实例代码
2017/04/22 NodeJs
es6学习笔记之Async函数的使用示例
2017/05/11 Javascript
JavaScript数据结构之双向链表和双向循环链表的实现
2017/11/28 Javascript
vue中的过滤器实例代码详解
2019/06/06 Javascript
vue 解决IOS10低版本白屏的问题
2020/11/17 Javascript
JavaScript 实现继承的几种方式
2021/02/19 Javascript
Python冒泡排序注意要点实例详解
2016/09/09 Python
Python中的命令行参数解析工具之docopt详解
2017/03/27 Python
python 计算积分图和haar特征的实例代码
2019/11/20 Python
浅谈html5标签css3的常用样式
2016/10/20 HTML / CSS
canvas里面如何基于随机点绘制一个多边形的方法
2018/06/13 HTML / CSS
Melijoe美国官网:法国奢侈童装购物网站
2017/04/19 全球购物
国贸专业自荐信范文
2014/03/02 职场文书
调解协议书
2014/04/16 职场文书
青春演讲稿范文
2014/05/08 职场文书
乡镇党的群众路线教育实践活动个人整改方案
2014/10/31 职场文书
推广普通话的宣传语
2015/07/13 职场文书
2016党员党课心得体会
2016/01/07 职场文书
python实现简单反弹球游戏
2021/04/12 Python
配置nginx 重定向到系统维护页面
2021/06/08 Servers
漫画「你在春天醒来」第10卷封面公开
2022/03/21 日漫
【TED出品】天梯非主流开心游1700 划水骑士
2022/03/31 魔兽争霸