在一些複雜的頁面中經常會用JavaScript處理一些DOM元素的動態效果,這種時候我們經常會用到一些元素位置和尺寸的計算,瀏覽器安全色性問題也是不可忽略的一部分,要想寫出預想效果的JavaScript代碼,我們需要瞭解一些基本知識。
基礎概念
為了方便理解,我們需要瞭解幾個基礎概念,每個HTML元素都有下列屬性
offsetWidth |
clientWidth |
scrollWidth |
offsetHeight |
clientHeight |
scrollHeight |
offsetLeft |
clientLeft |
scrollLeft |
offsetTop |
clientTop |
scrollTop |
為了理解這些屬性,我們需要知道HTML元素的實際內容有可能比分配用來容納內容的盒子更大,因此可能會出現捲軸,內容地區是視口,當實際內容比視口大的時候,需要把元素的捲軸位置考慮進去。
1. clientHeight和clientWidth用於描述元素內尺寸,是指 元素內容+內邊距 大小,不包括邊框(IE下實際包括)、外邊距、捲軸部分
2. offsetHeight和offsetWidth用於描述元素外尺寸,是指 元素內容+內邊距+邊框,不包括外邊距和捲軸部分
3. clientTop和clientLeft返回內邊距的邊緣和邊框的外邊緣之間的水平和垂直距離,也就是左,上邊框寬度
4. offsetTop和offsetLeft表示該元素的左上方(邊框外邊緣)與已定位的父容器(offsetParent對象)左上方的距離
5. offsetParent對象是指元素最近的定位(relative,absolute)祖先元素,遞迴上溯,如果沒有祖先元素是定位的話,會返回null
寫個小例子方便理解
複製代碼 代碼如下:<div id="divParent" style="padding: 8px; background-color: #aaa; position: relative;">
<div id="divDisplay" style="background-color: #0f0; margin: 30px; padding: 10px;
height: 200px; width: 200px; border: solid 3px #f00;">
</div>
</div>
複製代碼 代碼如下:<script type="text/javascript">
var div = document.getElementById('divDisplay');
var clientHeight = div.clientHeight;
var clientWidth = div.clientWidth;
div.innerHTML += 'clientHeight: ' + clientHeight + '<br/>';
div.innerHTML += 'clientWidth: ' + clientWidth + '<br/>';
var clientLeft = div.clientLeft;
var clientTop = div.clientTop;
div.innerHTML += 'clientLeft: ' + clientLeft + '<br/>';
div.innerHTML += 'clientTop: ' + clientTop + '<br/>';
var offsetHeight = div.offsetHeight;
var offsetWidth = div.offsetWidth;
div.innerHTML += 'offsetHeight: ' + offsetHeight + '<br/>';
div.innerHTML += 'offsetWidth: ' + offsetWidth + '<br/>';
var offsetLeft = div.offsetLeft;
var offsetTop = div.offsetTop;
div.innerHTML += 'offsetLeft: ' + offsetLeft + '<br/>';
div.innerHTML += 'offsetTop: ' + offsetTop + '<br/>';
var offsetParent = div.offsetParent;
div.innerHTML += 'offsetParent: ' + offsetParent.id + '<br/>';
</script>
效果
我們可以看到,clientHeight就是div的高度+上下各10px的padding,clientWidth同理
而clientLeft和ClientTop即為div左、上邊框寬度
offsetHeight是clientHeight+上下個3px的邊框寬度之和,offsetWidth同理
offsetTop是div 30px的 maggin+offsetparent 8px的 padding,offsetLeft同理
6. scrollWidth和scrollHeight是元素的內容地區加上內邊距加上溢出尺寸,當內容正好和內容地區匹配沒有溢出時,這些屬性與clientWidth和clientHeight相等
7. scrollLeft和scrollTop是指元素捲軸位置,它們是可寫的
下面寫個簡單例子理解
複製代碼 代碼如下:<div id="divParent" style="background-color: #aaa; padding:8px; border:solid 7px #000; height:200px; width:500px; overflow:auto;">
<div id="divDisplay" style="background-color: #0f0; margin: 30px; padding: 10px;
height: 400px; width: 200px; border: solid 3px #f00;">
</div>
</div>
複製代碼 代碼如下:<script type="text/javascript">
var divP = document.getElementById('divParent');
var divD = document.getElementById('divDisplay');
var scrollHeight = divP.scrollHeight;
var scrollWidth = divP.scrollWidth;
divD.innerHTML += 'scrollHeight: ' + scrollHeight + '<br/>';
divD.innerHTML += 'scrollWidth: ' + scrollWidth + '<br/>';
</script>
在FireFox和IE10(IE10以下版本盒模型和W3C標準不一致,不加討論,相容性問題下面會介紹到)下得到結果scrollHeight: 494,而在Chrome和Safari下得到結果scrollHeight: 502,差了8px,根據8可以簡單推測是divParent的padding,來算一下是不是
我們可以看看它們的結果是怎麼來的,首先scrollHeight肯定包含了divDisplay所需的高度 400px的高度+上下各10px的padding+上下各3px的border+上下各30px的margin,這樣
400+10*2+3*2+30*2=486
這樣486+8=494, 486+8*2=502果真是這樣,在FireFox和IE10下沒有計算下padding
有了這些基礎知識後,我們就可以計算元素的位置和尺寸了。
相對於文檔與視口的座標
當我們計算一個DOM元素位置也就是座標的時候,會涉及到兩種座標系,文檔座標和視口座標。
我們經常用到的document就是整個頁面部分,而不僅僅是視窗可見部分,還包括因為視窗大小限制而出現捲軸的部分,它的左上方就是我們所謂相對於文檔座標的原點。
視口是顯示文檔內容的瀏覽器的一部分,它不包括瀏覽器外殼(菜單,工具列,狀態列等),也就是當前視窗顯示頁面部分,不包括捲軸。
如果文檔比視口小,說明沒有出現滾動,文檔左上方和視口左上方相同,一般來講在兩種座標系之間進行切換,需要加上或減去滾動的位移量(scroll offset)。
為了在座標系之間進行轉換,我們需要判定瀏覽器視窗的捲軸位置。window對象的pageXoffset和pageYoffset提供這些值,IE 8及更早版本除外。也可以通過scrollLeft和scrollTop屬性獲得捲軸位置,正常情況下通過查詢文檔根節點(document.documentElement)來獲得這些屬性值,但在怪異模式下必須通過文檔的body上查詢。
複製代碼 代碼如下:function getScrollOffsets(w) {
var w = w || window;
if (w.pageXoffset != null) {
return { x: w.pageXoffset, y: pageYoffset };
}
var d = w.document;
if (document.compatMode == "CSS1Compat")
return { x: d.documentElement.scrollLeft, y: d.documentElement.scrollTop };
return { x: d.body.scrollLeft, y: d.body.scrollTop };
}
有時候能夠判斷視口的尺寸也是非常有用的
複製代碼 代碼如下:function getViewPortSize(w) {
var w = w || window;
if (w.innerWidth != null)
return { w: w.innerWidth, h: w.innerHeight };
var d = w.document;
if (document.compatMode == "CSS1Compat")
return { w: d.documentElement.clientWidth, h: d.documentElement.clientHeight };
return { w: d.body.clientWidth, h: d.body.clientHeight };
}
文檔座標
任何HTML元素都擁有offectLeft和offectTop屬性返回元素的X和Y座標,對於很多元素,這些值是文檔座標,但是對於以定位元素後代及一些其他元素(表格單元),返回相對於祖先的座標。我們可以通過簡單的遞迴上溯累加計算
複製代碼 代碼如下:function getElementPosition(e) {
var x = 0, y = 0;
while (e != null) {
x += e.offsetLeft;
y += e, offsetTop;
e = e.offsetParent;
}
return { x: x, y: y };
}
儘管如此,這個函數也不總是計算正確的值,當文檔中含有捲軸的時候這個方法就不能正常工作了,我們只能在沒有捲軸的情況下使用這個方法,不過我們用這個原理算出一些元素相對於某個父元素的座標。
視口座標
計算視口座標就相對簡單了很多,可以通過調用元素的getBoundingClientRect方法。方法返回一個有left、right、top、bottom屬性的對象,分別表示元素四個位置的相對於視口的座標。getBoundingClientRect所返回的座標包含元素的內邊距和邊框,不包含外邊距。相容性很好,非常好用
元素尺寸
由上面計算座標方法,我們可以方便得出元素尺寸。在符合W3C標準的瀏覽器中getBoundingClientRect返回的對象還包括width和height,但在原始IE中未實現,但是通過返回對象的right-left和bottom-top可以方便計算出。