JavaScript中计算网页中某个元素的位置
由于项目的需要,测试中需要对网页元素进行截图,以确保它看上去没有问题。之前我写过一篇文章介绍过一种方法,先使用WebDriver进行全屏截图,然后根据目标元素(DOMElement)所在的位置,再对截下来的图片进行剪裁,保留我们需要的位置即可。
那段代码一直都工作得很好,直到我知道了一个东西:iframe。iframe(普通的frame也是一样的,不过frame现在不太常见,这里只用iframe举例)中的内容被视为一个独立的网页,连Window对象也是和它的父级网页分开的。而WebDriver中的WebElement.getLocation()方法只能返回这个WebElement和它所在的Window的位置关系,它的实现没什么问题,但全屏截图不仅包含了iframe的内容,可能也包含了它的父级页面的内容,剪裁的时候需要知道目标元素在截图中的位置。那么问题来了,挖掘机技术哪家强?如何计算一个元素相对于截图的位置?
这个问题还要分类讨论,原因是:Chrome和Firefox中截图的行为是不一样的。Chrome的截图是当前可见(viewport)的网页内容,比方说,当网页的实际大小超过Chrome窗口大小时,根据滚动条的位置不同,窗口中显示的内容不同,Chrome的截图就是显示出来的内容。于是我们要计算目标元素相对于当前可见内容的位置。而Firefox用了一个方法,可以截到整个网页的内容,无视当前窗口大小。于是对于Firefox我们要计算元素的绝对位置(AbsolutePosition)。
获得一个元素的位置,需要用到一个方法:Element.getBoundingClientRect()。这个方法返回这个元素相对于它所处的Windows在当前可见内容的位置,用top、left、right、bottom四个值来表示。我们只关心其中的top和left,至于剪裁的尺寸,我们可以通过元素本身的长和宽来得到,不需要计算。要计算目标元素对于顶级Window的位置,我们只需要依次加上它的父级Window的top和left即可。代码如下:
functioncalcViewportLocation(element){ varcurrentWindow=window; varrect=element.getBoundingClientRect();//元素的位置 vartop=rect.top; varleft=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也可以特殊处理的:
functioncalcAbsolutLocation(element){ vartop=0; varleft=0; varcurrentWindow=window; while(element!=null){ rect=element.getBoundingClientRect(); varpageYOffset=currentWindow.pageYOffset; varpageXOffset=currentWindow.pageXOffset; if(typeofpageYOffset==='undefined'){//IE8 currentDocument=currentWindow.document; varbodyElement=(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中对特定元素截图。