相信搞前端开发的朋友们都遇到过这个问题,网上有很多讨论它的文章,但似乎都没有给出一个很完美的解决方案。本文试图用传统的递归offsetLeft,offsetTop的方法来获得元素的绝对坐标,并通过这个过程加深对DOM盒模型的理解,将其中易混淆的属性如offsetLeft,scrollLeft分辨开来。
1. 支持的浏览器
针对目前的情况,支持ie6+, 以及firefox,chrome,opera,safari的最新版本(这里偷个懒,所以具体支持到什么版本没有测试,但是一般而言,国内使用这些浏览器的用户都会选择较新的版本,应该没有问题)。
2. 测试页面编写
测试页面有内嵌的CSS元素,以及引入的Javascript代码,即getOffset函数。测试页面有以下几个元素需要注意,首先就是id为target的元素,它是我们要获取绝对坐标的元素,它存在一个div元素中,这个div元素随着测试的进行,它的position是否为静态,是否又边框,是否又滚动条会发生变化,从而测试我们函数的健壮性。每次测试的触发都是通过对id为button的元素单击完成的,测试完成后,需要一个小元素去证明获取的是否正确,即id为testResult的元素。id为coord元素也只是起一个坐标指示的作用,您可以忽略它。下面是html文件最终的测试版本,您如果用该文件进行测试,在接下来的每一小节的描述中,您需要自行修改CSS部分。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<title>测试最完整的获取页面Dom元素的绝对位置的函数-Get Absolute Positon of DOM Element by Javascript </title>
<script type="text/javascript" src="getOffset.js"></script>
<style type="text/css">
html, body {
margin: 0;
padding: 0;
}
html {
}
body {
min-height: 1200px;
border: 10px solid red;
margin: 30px;
}
.wrap1 {
background-color: #777;
}
.wrap2 {
background-color: #999;
margin-left: 20px;
}
.wrap3 {
background-color: #BBB;
margin-left: 20px;
padding-left: 10px;
height: 400px;
width: 400px;
}
.pos-static {
position: static;
}
.pos-nonstatic {
position: relative;
left: 50px;
top: 10px;
}
.bd {
border: 10px solid yellow;
}
.non-bd {
border:none;
}
.scrl {
overflow: scroll;
height: 200px;
}
.non-scrl {
overflow: auto;
}
#target {
background: blue;
}
#testResult {
width: 10px;
height: 10px;
position:absolute;
background-color:green;
left:0px;
top:0px;
z-index: 100;
}
#coord {
width: 10px;
height: 210px;
position: absolute;
left: 200px;
top:0px;
background: green;
z-index: 400;
}
#seperator {
height: 200px;
background: white;
}
</style>
</head>
<body>
<div id="testResult"></div>
<div id="coord"></div>
<div class="wrap1">
<div class="wrap2 pos-nonstatic">
<div class="wrap3 bd scrl">
<div id="seperator">
</div>
<div id="target">My position</div>
</div>
</div>
<h1>--------------------------------------------------</h1>
</div>
<div id="button" style="border:1px solid;">计算 | getOffset_geetest</div>
<div id="result">
</div>
<script type="text/javascript">
var button = document.getElementById('button');
var target = document.getElementById('target');
var result = document.getElementById('result');
var testResult = document.getElementById('testResult');
button.onclick = function () {
var str = ('Ele属性: offsetLeft:' + target.offsetLeft + ' offsetTop:' +target.offsetTop + ' scrollLeft:' + target.scrollLeft + ' scrollTop:' +target.scrollTop + '<br />');
var pos = getOffset_geetest_ex(target);
str += ('getOffset:' + pos.left + ', ' + pos.top +'<br />');
result.innerHTML = str;
testResult.style.left = pos.left + "px";
testResult.style.top = pos.top + "px";
}
</script>
</body>
</html>
3. 最原始的getOffset函数,直接用offsetTop 和 offsetLeft递
function getOffset(el) {
var _x = 0, _y = 0;
while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
_x += el.offsetLeft - el.scrollLeft;
_y += el.offsetTop - el.scrollTop;
el = el.offsetParent;
}
return { top: _y, left: _x };
}
4. 测试第一步,target的父元素有边框外,无滚动,body有边框,border为10,且body的position为relative。
可以看到初始情况下,用于测试结果的testResult小方块正好在body的框内。如下图所示
测试结果如下所示:
offsetTop | 最终的top | offsetLeft | 最终的left | 偏移 | |
Chrome | 220 | 210 | 70 | 60 | √ |
Firefox | 210 | 210 | 60 | 60 | √ |
Opera | 220 | 210 | 70 | 60 | √ |
IE(高) | 210 | 210 | 60 | 60 | √ |
IE(低) | 200 | 210 | 10 | 60 | √ |
通过上表可以看出,Opera和Chrome浏览器的表现很一致,即他们的offsetLeft和offsetTop包含了body元素的边框大小,而Firefox和ie则没有包含。ie低版本是因为它的offsetLeft指向了它的父元素(position为static)。如果此时,设置body元素的margin,padding都没有任何影响。
(待续...)