相信搞前端开发的朋友们都遇到过这个问题,网上有很多讨论它的文章,但似乎都没有给出一个很完美的解决方案。本文试图用传统的递归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都没有任何影响。
(待续...)