前面的話
偏移量(offset dimension)是javascript中的一個重要的概念。涉及到偏移量的主要是offsetLeft、offsetTop、offsetHeight、offsetWidth這四個屬性。當然,還有一個偏移參照——定位父級offsetParent。本文將詳細介紹該部分內容
定位父級
在理解偏移大小之前,首先要理解offsetParent。人們並沒有把offsetParent翻譯爲偏移父級,而是翻譯成定位父級,很大原因是offsetParent與定位有關
定位父級offsetParent的定義是:與當前元素最近的經過定位(position不等於static)的父級元素,主要分爲下列幾種情況
【1】元素自身有fixed定位,offsetParent的結果爲null
當元素自身有fixed固定定位時,我們知道固定定位的元素相對於視口進行定位,此時沒有定位父級,offsetParent的結果爲null
[注意]firefox瀏覽器有兼容性問題
<div id="test" style="position:fixed"></div> <script> //firefox並沒有考慮固定定位的問題,返回<body>,其他瀏覽器都返回null console.log(test.offsetParent); </script>
【2】元素自身無fixed定位,且父級元素都未經過定位,offsetParent的結果爲<body>
<div id="test"></div> <script> console.log(test.offsetParent);//<body> </script>
【3】元素自身無fixed定位,且父級元素存在經過定位的元素,offsetParent的結果爲離自身元素最近的經過定位的父級元素
<div id="div0" style="position:absolute;"> <div id="div1" style="position:absolute;"> <div id='test'></div> </div> </div> <script> console.log(test.offsetParent); //<div id="div1"> </script>
【4】<body>元素的offsetParent是null
console.log(document.body.offsetParent);//null
IE7-瀏覽器Bug
對於定位父級offsetParent來說,IE7-瀏覽器存在以下bug
【bug1】當元素本身經過絕對定位或相對定位,且父級元素無經過定位的元素時,IE7-瀏覽器下,offsetParent是<html>
<div id="test" style="position:absolute;"></div> <script> //IE7-瀏覽器返回<html>,其他瀏覽器返回<body> console.log(test.offsetParent); </script>
<div id="test" style="position:relative;"></div> <script> //IE7-瀏覽器返回<html>,其他瀏覽器返回<body> console.log(test.offsetParent); </script>
<div id="test" style="position:fixed;"></div> <script> //firefox並沒有考慮固定定位的問題,返回<body>,其他瀏覽器都返回null console.log(test.offsetParent); </script>
【bug2】如果父級元素存在觸發haslayout的元素或經過定位的元素,且offsetParent的結果爲離自身元素最近的經過定位或觸發haslayout的父級元素
[注意]關於haslayout的詳細信息移步至此
<div id="div0" style="display:inline-block;"> <div id='test'></div> </div> <script> //IE7-瀏覽器返回<div id="div0">,其他瀏覽器返回<body> console.log(test.offsetParent); </script>
<div id="div0" style="position:absolute;"> <div id="div1" style="display:inline-block;"> <div id='test'></div> </div> </div> <script> //IE7-瀏覽器返回<div id="div1">,其他瀏覽器返回<div id="div0"> console.log(test.offsetParent); </script>
<div id="div0" style="display:inline-block;"> <div id="div1" style="position:absolute;"> <div id='test'></div> </div> </div> <script> //所有瀏覽器都返回<div id="div1"> console.log(test.offsetParent); </script>
偏移量
偏移量共包括offsetHeight、offsetWidth、offsetLeft、offsetTop這四個屬性
offsetWidth
offsetWidth表示元素在水平方向上佔用的空間大小,無單位(以像素px計)
offsetWidth = border-left-width + padding-left + width + padding-right + border-right-width;
offsetHeight
offsetHeight表示元素在垂直方向上佔用的空間大小,無單位(以像素px計)
offsetHeight = border-top-width + padding-top + height + padding-bottom + border-bottom-width
<div id="test" style="width:100px; height:100px; padding:10px; margin:10px; border:1px solid black;"></div> <script> //122=1+10+100+10+1 console.log(test.offsetWidth); console.log(test.offsetHeight); </script>
[注意]如果存在垂直滾動條,offsetWidth也包括垂直滾動條的寬度;如果存在水平滾動條,offsetHeight也包括水平滾動條的高度
<div id="test" style="width:100px; height:100px; padding:10px; margin:10px; border:1px solid black; overflow: scroll;"></div> <script> //IE7,IE8-,火狐瀏覽器將垂直滾動條的寬度計算在width寬度和height高度中,width和height的值仍然是100px; //而其他瀏覽器則把垂直滾動條的寬度從width寬度中移出,把水平滾動條的高度從height高度中移出,則滾動條寬度爲17px,width寬度和height高度爲剩下的83px if(window.getComputedStyle){ console.log(getComputedStyle(test).width,getComputedStyle(test).height)//83px }else{ console.log(test.currentStyle.width,test.currentStyle.height);//100px } //122=1+10+100+10+1 console.log(test.offsetWidth,test.offsetHeight); </script>
offsetTop
offsetTop表示元素的上外邊框至offsetParent元素的上內邊框之間的像素距離
offsetLeft
offsetLeft表示元素的左外邊框至offsetParent元素的左內邊框之間的像素距離
<div id="out" style="padding: 5px;position: relative;background-color: pink;margin: 6px;border:1px solid black"> <div id="test" style="width:100px; height:100px; margin:10px;background-color:green;"></div> </div> <script> //15=test.marginTop(10) + out.paddingTop(5) alert(test.offsetTop); //15=test.marginLeft(10) + out.paddingLeft(5) alert(test.offsetLeft); </script>
IE7-Bug
IE7-瀏覽器在offsetTop屬性的處理上存在bug
【1】若父級設置position: relative,則在IE7-瀏覽器下,offsetTop值爲offsetParent元素的padding-top值+border-top值
<div id="out" style="padding: 5px;position: relative;border:1px solid #f00"> <div id="test" style="width:100px; height:100px; margin:10px;"></div> </div> <script> //其他瀏覽器返回15(5+10),而IE7-瀏覽器返回6 console.log(test.offsetTop); </script>
【2】若父級設置position: aboslute(或其他觸發haslayout的條件),offsetTop值爲offsetParent元素的padding-top值和當前元素的marginTop值的較大值
<div id="out" style="padding: 5px;position:absolute;"> <div id="test" style="width:100px; height:100px; margin:10px;"></div> </div> <script> //其他瀏覽器返回15(5+10),而IE7-瀏覽器返回10(10和5的較大值) console.log(test.offsetTop); </script>
【3】若父級設置position: relative,offsetLeft值爲offsetParent元素的padding-left值+marginLeft值+border-left值
頁面偏移
要知道某個元素在頁面上的偏移量,將這個元素的offsetLeft和offsetTop與其offsetParent的相同屬性相加,並加上offsetParent的相應方向的邊框,如此循環直到根元素,就可以得到元素到頁面的偏移量
[注意]在默認情況下,IE8-瀏覽器下如果使用currentStyle()方法獲取div元素的邊框寬度都是medium,而如果使用clientLeft(或clientTop)獲取邊框寬度,則是實際的數值,而如果是body,html,則用currentStyle能獲取到實際邊框寬度,用clientLeft會變成medium
html,body{border: 0;} body{margin:0;}
function getElementLeft(element){
var parent = element.offsetParent;
var left = element.offsetLeft;
while(parent){
left+=parent.offsetLeft;parent = parent.offsetParent;
}
return left;
}
function getElementTop(element){
var actualTop = element.offsetTop;
var current = element.offsetParent;
var clientTop = 0;
while(current != null){
actualTop += current.offsetTop;
current = current.offsetParent;
}
return actualTop + 'px';
}
<div style="padding: 20px;border:1px solid black;position:absolute;"> <div id="test" style="width:100px; height:100px; margin:10px;"></div> </div> <script> //其他瀏覽器返回30(10+20),而IE7-瀏覽器返回20(20和10的較大值) console.log(getElementTop(test)); //所有瀏覽器返回30(10+20) console.log(getElementLeft(test)); </script>
注意事項
【1】所有偏移量屬性都是隻讀的
<div id="test" style="width:100px; height:100px; margin:10px;"></div> <script> console.log(test.offsetWidth);//100 //IE8-瀏覽器會報錯,其他瀏覽器則靜默失敗 test.offsetWidth = 10; console.log(test.offsetWidth);//100 </script>
【2】如果給元素設置了display:none,則它的偏移量屬性都爲0
<div id="test" style="width:100px; height:100px; margin:10px;display:none"></div> <script> console.log(test.offsetWidth);//0 console.log(test.offsetTop);//0 </script>
【3】每次訪問偏移量屬性都需要重新計算
<div id="test" style="width:100px; height:100px; margin:10px;"></div> <script> console.time("time"); for(var i = 0; i < 100000; i++){ var a = test.offsetWidth; } console.timeEnd('time');//65.129ms </script>
<div id="test" style="width:100px; height:100px; margin:10px;"></div> <script> console.time("time"); var a = test.offsetWidth; for(var i = 0; i < 100000; i++){ var b = a; } console.timeEnd('time');//1.428ms </script>
由上面代碼對比可知,重複訪問偏移量屬性需要耗費大量的性能,所以要儘量避免重複訪問這些屬性。如果需要重複訪問,則把它們的值保存在變量中,以提高性能