引言

  最近突然看到了有關(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è)元素:

iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

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 }

iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

clientWidthoffsetWidthscrollWidth
iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)  iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn) iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)
iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

   以上 DEMO 是常規(guī)情況下的區(qū)別,下面加上一個(gè)滾動(dòng)條我們們?cè)賮?lái)觀察以下:

clientWidthoffsetWidthscrollWidth
iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

注意這里不包括滾動(dòng)條的長(zhǎng)度

 iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn) iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

這里實(shí)際上相當(dāng)于內(nèi)容的寬度

iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn) 

iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

 二、clientTop、offsetTop、scrollTop 的區(qū)別

  我們使用以下公式:

  clientTop = border

  offsetTop = 元素邊框外圍至父元素邊框內(nèi)圍

  scrollTop = 元素可視區(qū)域頂部至實(shí)際內(nèi)容區(qū)域的頂部

  給定以下兩個(gè)元素 container 和 test

iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

 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 }

iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

clientTop offsetTopscrollTop
iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)
 
iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn) 
iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)
   

 三、獲取頁(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)元素的祖先元素):

iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

 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 })();

iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

  此方法可以獲取一個(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è)方法:

iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

 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 })();

iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

  通過(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ì)于瀏覽器窗口右下角的定位;

iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

 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 })();

iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

  現(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)了:

iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

 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