引言
最近突然看到了有關(guān)圖片懶加載的問(wèn)題,大致意思就是初始狀態(tài)下頁(yè)面只加載瀏覽器可視區(qū)域的圖片,剩余圖片在當(dāng)瀏覽器可視區(qū)域滾動(dòng)到其位置時(shí)才開(kāi)始加載。貌似現(xiàn)在許多大型網(wǎng)站都有實(shí)現(xiàn)懶加載,所以我便就此問(wèn)題思考了一下。首先第一個(gè)問(wèn)題是瀏覽器沒(méi)有相關(guān)的 API 方法可以檢測(cè)某個(gè)元素是否在可視區(qū)域,那么就只能我們?nèi)斯び?jì)算,所以這里就涉及到了元素長(zhǎng)寬,滾動(dòng)條位置的知識(shí)。本文涉及的到的知識(shí)有元素長(zhǎng)寬 clientWidth/offsetWidth/scrollWidth 的區(qū)別、以及 clientTop/offsetTop/scrollTop 的區(qū)別,并給了獲取元素坐標(biāo)的源代碼。
一、 clientWidth、offsetWidth、scrollWidth 的區(qū)別
通常大家獲取元素的長(zhǎng)寬的時(shí)候都會(huì)使用一些框架封裝好的方法,比如 jQuery.prototype.width() ,這些框架使用起來(lái)方便快捷,不過(guò)其中涉及到的知識(shí)還是非常多的,關(guān)于元素的長(zhǎng)寬,有多種的獲取方法,其代表的實(shí)際意義也是不同的。
簡(jiǎn)單來(lái)說(shuō)可以使用下列公式:
clientWidth = width(可視區(qū)) + padding
offsetWidth = width(可視區(qū)) + padding + border
scrollWidth = width(內(nèi)容區(qū))
假設(shè)有我們以下一個(gè)元素:
1 #test {2 width: 100px;3 height: 100px;4 margin: 10px;5 border: 10px solid #293482;6 padding: 10px;7 background-color: yellow;8 overflow: auto;9 }
clientWidth | offsetWidth | scrollWidth |
以上 DEMO 是常規(guī)情況下的區(qū)別,下面加上一個(gè)滾動(dòng)條我們們?cè)賮?lái)觀察以下:
clientWidth | offsetWidth | scrollWidth |
注意這里不包括滾動(dòng)條的長(zhǎng)度 | 這里實(shí)際上相當(dāng)于內(nèi)容的寬度
| |
二、clientTop、offsetTop、scrollTop 的區(qū)別
我們使用以下公式:
clientTop = border
offsetTop = 元素邊框外圍至父元素邊框內(nèi)圍
scrollTop = 元素可視區(qū)域頂部至實(shí)際內(nèi)容區(qū)域的頂部
給定以下兩個(gè)元素 container 和 test
1 #container { 2 background-color: #F08D8D; 3 padding: 10px; 4 } 5 #test { 6 position: relative; 7 top: 10px; 8 width: 100px; 9 height: 100px;10 margin: 20px;11 border: 15px solid #293482;12 padding: 10px;13 background-color: yellow;14 }
clientTop | offsetTop | scrollTop |
三、獲取頁(yè)面元素絕對(duì)定位坐標(biāo)
有了以上知識(shí)基礎(chǔ)之后,我們現(xiàn)在需要考慮的問(wèn)題是,如何獲取頁(yè)面元素的絕對(duì)位置,也就是在文檔流內(nèi)容區(qū)的位置。我們知道,元素的 offsetTop 屬性可以獲取當(dāng)前元素邊框外圍至父元素邊框內(nèi)圍的的距離,clientTop 可以獲取元素邊框的寬度。那么現(xiàn)在用一個(gè)遞歸的公式就可以求得當(dāng)前元素在頁(yè)面中的絕對(duì)位置:
Element.absoluteTop = Element.parent.absoluteTop + Element.offsetTop + Element.clientTop;
同理,我們用參照元素的長(zhǎng)寬減去 left 和 top 和定位,即可得到 right 和 bottom 的定位;
所以我們可以編寫(xiě)以下工具來(lái)獲取元素的絕對(duì)位置,也就是在內(nèi)容區(qū)的定位(參照元素必須是目標(biāo)元素的祖先元素):
1 var Position = {}; 2 (function () { 3 Position.getAbsolute = function (reference, target) { 4 //因?yàn)槲覀儠?huì)將目標(biāo)元素的邊框納入遞歸公式中,這里先減去對(duì)應(yīng)的值 5 var result = { 6 left: -target.clientLeft, 7 top: -target.clientTop 8 } 9 var node = target;10 while(node != reference && node != document){11 result.left = result.left + node.offsetLeft + node.clientLeft;12 result.top = result.top + node.offsetTop + node.clientTop;13 node = node.parentNode;14 }15 if(isNaN(reference.scrollLeft)){16 result.right = document.documentElement.scrollWidth - result.left;17 result.bottom = document.documentElement.scrollHeight - result.top;18 }else {19 result.right = reference.scrollWidth - result.left;20 result.bottom = reference.scrollHeight - result.top;21 }22 return result;23 }24 })();
此方法可以獲取一個(gè)元素相對(duì)于一個(gè)父元素的定位,如果要獲取元素在整張頁(yè)面,直接傳入 document 即可:
1 Position.getAbsolute(document, targetNode); //{left: left, right: right, top: top, bottom: bottom}
四、獲取元素的可視區(qū)定位坐標(biāo)
在上一小節(jié)中,我們封裝了一個(gè)函數(shù),這個(gè)函數(shù)可以用來(lái)獲取一個(gè)元素的相對(duì)于一個(gè)祖先元素的絕對(duì)定位坐標(biāo),在這一小節(jié)中,我們來(lái)獲取元素相對(duì)于瀏覽器窗口可視區(qū)域的定位坐標(biāo)。在上一個(gè)函數(shù)中,我們可以獲取一個(gè)元素在 document 當(dāng)中的定位,還記得我們?cè)诘诙」?jié)中的 scrollTop 屬性嗎?該屬性可以獲取滾動(dòng)窗口可視區(qū)域頂端距離內(nèi)容區(qū)頂端的距離,我們用元素的絕對(duì)定位坐標(biāo)減去 document 的滾動(dòng)定位就是我們想要的瀏覽器窗口定位啦(相對(duì)于瀏覽器左上角):
ViewportTop = Element.absoluteTop - document.body.scrollTop;
這里需要注意一個(gè)兼容性的問(wèn)題,在 Chrome 中可以用 document.body.scrollTop 和 window.pageYOffset,IE 7/8 只能通過(guò) document.documentElement.scrollTop 獲取, FireFox 和 IE9+ 可以用 document.documentElement.scrollTop 和 window.pageYOffset 獲取,Safari 需要 window.pageYOffset 獲取。所以這里我們需要做一下瀏覽器兼容:
scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
注意這里的順序,在 IE7/8 中 window.pageYOffset 是 undefined ,document.body.scrollTop 在任何瀏覽器中都有,只是不支持的值為 0,如果表達(dá)式返回 undefined ,會(huì)影響后面的計(jì)算操作。而 || 運(yùn)算符是一個(gè)短路取真運(yùn)算符,所以我們要所有瀏覽器都有的 document.body.scrollTop 方法放在最后,關(guān)于 || 運(yùn)算符的問(wèn)題,可以參考 《探尋 JavaScript 邏輯運(yùn)算符(與、或)的真諦》。
我們?cè)趧偛诺墓ぞ呱咸砑右粋€(gè)方法:
1 var Position = {}; 2 (function () { 3 Position.getAbsolute = function (reference, target) { 4 var result = { 5 left: -target.clientLeft, 6 top: -target.clientTop 7 } 8 var node = target; 9 while(node != reference && node != document){10 result.left = result.left + node.offsetLeft + node.clientLeft;11 result.top = result.top + node.offsetTop + node.clientTop;12 node = node.parentNode;13 }14 if(isNaN(reference.scrollLeft)){15 result.right = document.documentElement.scrollWidth - result.left;16 result.bottom = document.documentElement.scrollHeight - result.top;17 }else {18 result.right = reference.scrollWidth - result.left;19 result.bottom = reference.scrollHeight - result.top;20 }21 return result;22 }23 Position.getViewport = function (target) {24 var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;25 var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft;26 var absolutePosi = this.getAbsolute(document, target);27 var Viewport = {28 left: absolutePosi.left - scrollLeft,29 top: absolutePosi.top - scrollTop,30 }31 return Viewport;32 }33 })();
通過(guò) Position.getViewport 方法可以獲取元素相對(duì)于瀏覽器窗口的定位:
1 Postion.getViewport(targetNode); //{left: left, top: top}
五、判斷可視區(qū)域
在上面的幾個(gè)方法中,我們可以獲取元素的文檔流定位和視窗定位,不過(guò)這還是不能判斷一個(gè)元素是否在可視區(qū)域內(nèi),因?yàn)橐暣岸ㄎ豢梢允欠浅4蟮臄?shù)字,這樣元素就在視窗的后面。這里我們需要使用瀏覽器視窗高度 window.innerHeight 屬性,在 IE8 以下需要用 document.documentElement.clientHeight 來(lái)獲取。
windowHeight = window.innerHeight || document.documentElement.clientHeight;
現(xiàn)在,我們用窗口的高度,減去相對(duì)于瀏覽器窗口的定位,即可獲取相對(duì)于瀏覽器窗口右下角的定位;
1 var Position = {}; 2 (function () { 3 Position.getAbsolute = function (reference, target) { 4 var result = { 5 left: -target.clientLeft, 6 top: -target.clientTop 7 } 8 var node = target; 9 while(node != reference && node != document){10 result.left = result.left + node.offsetLeft + node.clientLeft;11 result.top = result.top + node.offsetTop + node.clientTop;12 node = node.parentNode;13 }14 if(isNaN(reference.scrollLeft)){15 result.right = document.documentElement.scrollWidth - result.left;16 result.bottom = document.documentElement.scrollHeight - result.top;17 }else {18 result.right = reference.scrollWidth - result.left;19 result.bottom = reference.scrollHeight - result.top;20 }21 return result;22 }23 Position.getViewport = function (target) {24 var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;25 var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft;26 var windowHeight = window.innerHeight || document.documentElement.offsetHeight;27 var windowWidth = window.innerWidth || document.documentElement.offsetWidth;28 var absolutePosi = this.getAbsolute(document, target);29 var Viewport = {30 left: absolutePosi.left - scrollLeft,31 top: absolutePosi.top - scrollTop,32 right: windowWidth - (absolutePosi.left - scrollLeft),33 bottom: windowHeight - (absolutePosi.top - scrollTop)34 }35 return Viewport;36 }37 })();
現(xiàn)在我們使用 Position.getViewport(targetNode) 方法可以獲取元素左上角相對(duì)于窗口4個(gè)方向的定位:
1 Position.getViewport(targetNode); //{left: left, top: top, right: right, bottom: bottom}
有了這個(gè)方法,現(xiàn)在就可以真正的判斷元素是否在可視區(qū)域內(nèi)了:
1 var Position = {}; 2 (function () { 3 Position.getAbsolute = function (reference, target) { 4 //因?yàn)槲覀儠?huì)將目標(biāo)元素的邊框納入遞歸公式中,這里先減去對(duì)應(yīng)的值 5 var result = { 6 left: -target.clientLeft, 7 top: -target.clientTop 8 } 9 var node = target;10 while(node != reference && node != document){11 result.left = result.left + node.offsetLeft + nod
http://www.cnblogs.com/dong-xu/p/7150715.html