获取元素相对浏览器窗口的偏移坐标
元素坐标:
- 浏览器坐标系统
- 使用offsetParent属性获取元素的坐标
- 使用w3c标准属性elem.getBoundingClientRect获取元素的坐标
- 结合以上两种方式,实现兼容浏览器的获取坐标方式
- 摘要
*在浏览器中存在两个坐标系统:*
- 相对文档——坐标远点在页面的左上角
- 相对可视化窗口——坐标原点在可视窗口的左上角
1. 坐标系统
当页面没有滚动时,窗口和页面共享相同的坐标原点,如下图所示:
滚动后,页面相对可视窗口发生了偏移如下图:
事实上,在这两种坐标系统间相互转化很简单,页面坐标是窗口坐标加上页面滚动偏移
大部分时候,仅仅使用页面坐标,因为他们滚动后维持相同。
2. 使用offsetParent属性获取元素的坐标
元素的坐标是元素左上角的坐标,不幸的是没有一个单一的属性可以获取到它的值,但可以是哟个offsetTop/offsetLeft和offsetParent计算它的值。
一种计算元素的绝对坐标的方式是遍历offsetParent链,并对遍历中的每个定位父元素的offsetLeft/offsetTop求和,如下所示:
function getOffsetSum(elem) {
var top=0, left=0
while(elem) {
top = top + parseInt(elem.offsetTop)
left = left + parseInt(elem.offsetLeft)
elem = elem.offsetParent
}
return {top: top, left: left}
}
但这种方式有两个缺陷:
1. 它存在bug,不同的浏览器存在不同的问题,当考虑边框和滚动的时候会出现问题。
2. 它计算起来很慢,每次都要遍历整个offsetParents链
可以写一个跨浏览器修复bug的方法,但先让我们来看另外一种标准做法,该方法在IE6+,firefox3+ OPera ,和safrari及chrome中也支持
3. 正确的方式:elem.getBoundingClientRect
这个方式在W3C标准下有描述,大多数现代浏览器都支持,包括IE
它返回一个包裹元素的矩形框,这个矩形框以top , left , right , bottom四个属性给出。
这四个数字代表了矩形的左上角和右下角的四个坐标,例如:
<input id="brTest" type="button" value="Show button.getBoundingClientRect()" onclick='showRect(this)'/>
<script>
function showRect(elem) {
var r = elem.getBoundingClientRect()
alert("Top/Left: "+r.top+" / "+r.left)
alert("Right/Bottom: "+r.right+" / "+r.bottom)
}
</script>
这个坐标是相对窗口,而不是相对文档。
让我们来看一个新版本的计算坐标的方式:使用getBoundingClientRect
function getOffsetRect(elem) {
var box = elem.getBoundingClientRect()
var body = document.body
var docElem = document.documentElement
var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop
var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft
var clientTop = docElem.clientTop || body.clientTop || 0
var clientLeft = docElem.clientLeft || body.clientLeft || 0
var top = box.top + scrollTop - clientTop
var left = box.left + scrollLeft - clientLeft
return { top: Math.round(top), left: Math.round(left) }
}
计算步骤如下:
- 获取到包含的矩形框
- 计算页面的滚动,除了IE<9外,所有浏览器都支持pageXOffset / pageYOffset,在IE中,当设置了DOCTYPE时,滚动的值可以通过documentElement获得,否则使用body对象获取。
- 在IE中文档会相对左上角偏移几个像素,需要去掉它,减去clientTop和clientLeft
- 矩形框的坐标加上页面相对窗口滚动的坐标,减去偏移的坐标,就得到了元素相对整个文档的坐标。
4. 跨浏览器兼容的解决方案:
有很多js库结合两种方式,获取元素的坐标:
function getOffset(elem) {
if (elem.getBoundingClientRect) {
return getOffsetRect(elem)
} else { // old browser
return getOffsetSum(elem)
}
}