在web開發(fā)中,,不可避免遇到要計算元素大小以及位置的問題,解決這類問題的方法是利用DOM提供的一些API結合兼容性處理來,,所有內容大概分3篇左右的文章的來說明,。本文作為第一篇,介紹DOM提供的與尺寸大小相關的DOM屬性,,提供一些兼容性處理的方法,,并結合常見的場景說明如何正確運用這些屬性。 1. 正確理解offsetWidth,、clientWidth,、scrollWidth及相應的height屬性假設某一個元素的橫縱向滾動條都拖動到最末端,則offsetWidth,、clientWidth,、scrollWidth等屬性相應的范圍如下圖所示: 1)offsetWidth ,offsetHeight對應的是盒模型的寬度和高度,,這兩個值跟我們使用chrome審查元素時看到的尺寸一致: 2)scrollWidth,,與scrollHeight對應的是滾動區(qū)域的寬度和高度,,但是不包含滾動條的寬度!滾動區(qū)域由padding和content組成,。 3)clientWidth,,clientHeight對應的是盒模型除去邊框后的那部分區(qū)域的寬度和高度。 4)任何一個DOM元素,,都可以通過以下api快速得到offsetWidth,,clientWidth,scrollWidh及相關的height屬性: //domE為一個DOM Html Element對象 domE.scrollWidth domE.scrollHeight domE.clientWidth domE.clientHeight domE.offsetWidth domE.offsetHeight 5)這些屬性在現代瀏覽器包括pc和mobile上幾乎沒有兼容性問題,,可以放心使用,。如果你想了解詳細的兼容性規(guī)則,可以參考下面的2篇文章: W3C DOM Compatibility - CSS Object Model View cssom視圖模式cssom-view-module相關整理與介紹 下面針對普通html元素,,html根元素和body元素的以上相關屬性一一測試,,以便驗證前面的結論,總結一些可在實際編碼過程中直接使用的經驗技巧,。之所以要區(qū)分普通html元素,,html根元素和body元素,是因為前面的理論,,在html根元素和body元素會有一些怪異之處,,需要小心處理。 注: 1,、為了減少篇幅,,測試貼出的代碼不是完整的代碼,但不影響學習參考,,另外文中給出的測試結果都是在chrome(版本:45.0)下運行得出的,,在測試結果有差異的情況下,還會給出IE9,,IE10,,IE11,firefox(版本:42.0),,opera(版本:34.0)的測試結果,,沒有差異的會在測試結果中說明,不考慮IE8及以下,。 2,、safari因為設備限制暫不測試,另外它跟chrome內核相同,,對標準支持的可靠性差不到哪去。 3,、老版本的chrome,firefox,opera也因為設備的限制無法測試,,不過從瀏覽器對標準的支持程度考慮,,這三個瀏覽器在很早的版本開始對W3C的標準都是比較規(guī)矩的,加之這些瀏覽器更新?lián)Q代的速度較快,,現在市面上這些瀏覽器主流的版本也都是較新的,。 4、由于不考慮IE8及以下,,同時html現在都用html5,,所以document.compatMode = 'BackCompat' 的情況不考慮。不過盡管BackCompat模式是IE6類的瀏覽器引出的,,但是對于chrome,,firefox等也存在document.compatMode = 'BackCompat' 的情況,比如下面的這個網頁,,你用chrome打開,,并且在console中打印document.compatMode,你會發(fā)現它的值也是BackCompat(原因跟該頁面用的是html4.0的dtd有關,,如果換成html4.01的dtd就不會在chrome和firefox里出現該情況了):
測試一、驗證普通html元素(非body及html根元素)的offsetWidth,、clientWidth,、scrollWidth及相關height屬性: <style type="text/css"> html, body { margin: 0; } body { padding: 100px; } .box { overflow: scroll; width: 400px; height: 300px; padding: 20px; border: 10px solid #000; margin: 0 auto; box-sizing: content-box; } .box-2 { border: 1px solid #000; } </style> <body> <div class="box"> <div class="box-2">...</div> </div> </body> <script type="text/javascript"> var boxE = document.querySelectorAll('.box')[0]; console.log('scrollWidth:' + boxE.scrollWidth); console.log('scrollHeight:' + boxE.scrollHeight); console.log('clientWidth:' + boxE.clientWidth); console.log('clientHeight:' + boxE.clientHeight); console.log('offsetWidth :' + boxE.offsetWidth); console.log('offsetHeight:' + boxE.offsetHeight); </script> 在這個例子中,box元素有400*300的寬高,,20px的padding和10px的border,,chrome下對應的盒模型: js執(zhí)行結果: 從盒模型與js執(zhí)行結果可知: 1)offsetWidth與offsetHeight與chrome審查元素看到的尺寸完全一致; 2)clientWidth與clientHeight分別等于offsetWidth與offsetHeight減掉相應邊框(上下共20px,,左右共20px)和滾動條寬度后的值(chrome下滾動條寬度為17px),; 3)對于scrollWidth由于沒有發(fā)生橫向的溢出,同時由于overflow: scroll的原因,,scrollWidth 跟clientWidth相同,,但是沒有包含滾動條的寬度,,這也驗證了前面提出的結論,; 4)對于scrollHeight,在這個例子中,,它其實等于上下padding(共40px) + div.box-2的offsetHeight(1370px),,div.box-2: 5)以上測試還有一個css值得注意,,就是box-sizing,以上代碼中box-sizing設置為了content-box,,如果把它改成border-box,,結果也是類似的,因為offsetWidth,,clientWidth還有scrollWidth對應的區(qū)域不會發(fā)生改變,。 6)其它瀏覽器運行結果與1-5的結論一致,。
測試二、驗證html根元素和body元素的相關offset client scroll寬高屬性: <style type="text/css"> html, body { margin: 0; } body { border: 10px solid #D4D2D2; } .box { overflow: scroll; width: 400px; height: 300px; padding: 20px; border: 10px solid #000; margin: 0 auto; box-sizing: content-box; } .box-2 { border: 1px solid #000; } </style> <body> <div class="box"> <div class="box-2">...</div> </div> <div class="box"> <div class="box-2">...</div> </div> <div class="box"> <div class="box-2">...</div> </div> <div class="box"> <div class="box-2">...</div> </div> </body> <script> console.log('docE.scrollWidth:' + document.documentElement.scrollWidth); console.log('scrollHeight:' + document.documentElement.scrollHeight); console.log('docE.clientWidth:' + document.documentElement.clientWidth); console.log('docE.clientHeight:' + document.documentElement.clientHeight); console.log('docE.offsetWidth :' + document.documentElement.offsetWidth); console.log('docE.offsetHeight:' + document.documentElement.offsetHeight); console.log(''); console.log('body.scrollWidth:' + document.body.scrollWidth); console.log('body.scrollHeight:' + document.body.scrollHeight); console.log('body.clientWidth:' + document.body.clientWidth); console.log('body.clientHeight:' + document.body.clientHeight); console.log('body.offsetWidth :' + document.body.offsetWidth); console.log('body.offsetHeight:' + document.body.offsetHeight); </script> 在這個例子中,,body下一共有4個box元素(總高度為360 * 4 = 1440px),,body的寬是自適應的,body還有10px的border,,運行結果如下: 從這個結果可以看到: 1)body元素由于10px邊框的原因,,所以clientWidth比offsetWidth少了20px,這跟前面提到的理論是一致的,,但是不可思議的是body的scrollWidth/scrollHeight竟然等于它的offsetWidth/offsetHeight,,scrollWidth/scrollHeight是元素滾動區(qū)域的寬高度,按照前面給出的范圍圖來理解,,body的scrollWidth/scrollHeight應該小于它的offsetWidth/offsetHeight才對,; 2)docE的scrollWidth跟scrollHeight,應該等于body元素的offsetWidth跟offsetHeight,,從運行結果來看,,這一點是符合的,但是docE的clientWidth竟然等于它的offsetWidth,,按照范圍圖,,docE的clientWidth應該等于offsetWidth減去滾動條寬度才對。 其它的瀏覽器運行結果與chrome也有較大的差異: IE11: 1)IE11下body元素沒有出現chrome下body元素的問題 2)IE11下html根元素也有chrome類似的問題 IE10,,IE9: 1)IE10,9下body元素沒有出現chrome下body元素的問題 2)IE10,9下html根元素也沒有chrome類似的問題 firefox:與IE11運行結果一致,。 opera: 與chrome運行結果一致,可能是因為我這個版本的opera用的跟chrome一樣的webkit內核的原因,。
看起來IE9就跟IE10是最正常的,,實在是有點難以理解,網上搜索很久,,也沒有找到相關資料來說明這些差異,,最后也只能采取大膽假設的方式,猜測出幾個能解釋這些問題的原因: 1) 首先,,網頁整體的滾動,,跟普通html元素的滾動不一樣,普通html元素自身就是滾動對象,, 但是對于網頁來說,,滾動對象不一定是html根元素或者body元素。因為當body內容為空時,,body的高度是0,,html根元素的高度也是0,如果這個時候給html或body加上overflow: scroll的css,會看到滾動條還是出現瀏覽器窗口的右邊跟底邊,,所以對于網頁整體的滾動,,理論上,滾動對象應該是window,,而不是html元素或者body元素!但實際情況并非如此,,就測試的瀏覽器而言: 對于IE10,,IE9,它的滾動對象是html根元素,,所以它們的html根元素的offset會包含滾動條的寬度,; 對于其它瀏覽器,滾動對象是window,,所以它們的html根元素的offset不包含滾動條的寬度,。 2)第二,普通元素發(fā)生滾動時,,滾動內容=它的content區(qū)域+它的padding區(qū)域,,當網頁整體滾動時,滾動內容應該是html根元素,!但實際情況也并非如此,,就測試的瀏覽器而言: 對于IE9,IE10,,IE11,,firefox,它們的滾動區(qū)域是html根元素,,所以它們的documentElement的scrollWidth和scrollHeight始終表示網頁整體的滾動區(qū)域大?。?/p> 對于chrome和opera,,它們的滾動對象是body元素,,所以它們的body的scrollWidth和scrollHeight始終表示網頁整體的滾動區(qū)域大小,! 3)第三,,瀏覽器始終把documentElement.clientWidth和documentElement.clientHeight描述為網頁可視區(qū)域除去滾動條部分的大小,跟網頁內容沒有關系,! 以上的這些推斷也并非是毫無道理,,就拿滾動對象和滾動區(qū)域來說:chrome下如果要用js滾動頁面到某個位置,在不使用window.scrollTo的條件下,,就必須用document.body.scrollTop = xxx 來處理,,而設置document.documentElement.scrollTop無效,說明chrome的整體滾動區(qū)域是由body的滾動區(qū)域決定的;而IE11和火狐下如果要用js滾動頁面到某個位置,,在不使用window.scrollTo的條件下,,就必須用document.documentElement.scrollTop = xxx來處理,設置document.body.scrollTop無效,,說明IE11和火狐的整體滾動區(qū)域是由html根元素的滾動區(qū)域決定的,。 2. 利用JS準確獲取DOM對象的大小常見的場景有: 1)獲取整個網頁的可視區(qū)域的大小,不包括滾動條 2)獲取整個網頁的大小,,包括不可見的滾動區(qū)域 3)獲取一個普通html元素的大小 4)判斷元素或網頁有無出現滾動條 5)計算滾動條的寬度 下面針對這7個場景一一說明,,以下代碼均不考慮IE8及以下,不考慮html4,,另外請注意viewport的設置,,要保證在移動設備上visual viewport與layout viewport重合。 1)如何獲取整個網頁的可視區(qū)域的大小,,不包括滾動條 document.documentElement.clientWidth;
document.documentElement.clientHeight;
2)如何獲取整個網頁的大小,,包括不可見的滾動區(qū)域 function pageWidth() { var doc = document.documentElement, body = document.body; if (doc.clientWidth == window.innerWidth) { return doc["clientWidth"]; } return Math.max( body["scrollWidth"], doc["scrollWidth"], body["offsetWidth"], doc["clientWidth"] ); } function pageHeight() { var doc = document.documentElement, body = document.body; if (doc.clientHeight == window.innerHeight) { return doc["clientHeight"]; } return Math.max( body["scrollHeight"], doc["scrollHeight"], body["offsetHeight"], doc["clientHeight"] ); } 以上出現的window.innerWidth和window.innerHeight分別用來獲取網頁包括滾動條的可視區(qū)域的寬高,這也是一個兼容性不錯的方法,,不過從實際開發(fā)情況來看,,我們需要不包括滾動條的可視區(qū)域更多一些,所以在前面沒有單獨介紹,。另外在之前給出的PPK的博客中也有關于這兩個屬性的兼容性測試,,可以去了解。 3)如何獲取一個普通html元素的大小 簡單方法: docE.offsetWidth;
docE.offsetHeight;
利用getBoundingClientRect: var obj = docE.getBoundingClientRect(), elemWidth, elemHeight; if(obj) { if(obj.width) { elemWidth = obj.width; elemHeight = obj.height; } else { elemWidth = obj.right - obj.left; elemHeight = obj.bottom - obj.top; } } else { elemWidth = docE.offsetWidth; elemHeight = docE.offsetHeight; } getBoundingClientRect將在下篇文章中跟其它與位置有關的DOM屬性一起再詳細介紹,。 4)判斷元素或網頁有無出現滾動條 function scrollbarState(elem) { var docE = document.documentElement, body = document.body; if (!elem || elem === document || elem === docE || elem === body) { return { scrollbarX: docE.clientHeight < window.innerHeight, scrollbarY: docE.clientWidth < window.innerWidth } } if (typeof(Element) == 'function' && !(elem instanceof(Element) || !body.contains(elem))) { return { scrollbarX: false, scrollbarY: false }; } var elemStyle = elem.style, overflowStyle = { hidden: elemStyle.overflow == 'hidden', hiddenX: elemStyle.overflowX == 'hidden', hiddenY: elemStyle.overflowY == 'hidden', scroll: elemStyle.overflow == 'scroll', scrollX: elemStyle.overflowX == 'scroll', scrollY: elemStyle.overflowY == 'scroll' }; return { scrollbarX: overflowStyle.scroll || overflowStyle.scrollX || (!overflowStyle.hidden && !overflowStyle.hiddenX && elem.clientWidth < elem.scrollWidth), scrollbarY: overflowStyle.scroll || overflowStyle.scrollY || (!overflowStyle.hidden && !overflowStyle.hiddenY && elem.clientHeight < elem.scrollHeight) }; } 當x或y方向的overflow為scroll的時候,,該方向的scrollbarX為true,表示出現滾動條,。 5)計算滾動條的寬度 function scrollbarWidth() { var docE = document.documentElement, body = document.body, e = document.createElement('div'); e.style.cssText = 'position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll;'; body.appendChild(e); var _scrollbarWidth = e.offsetWidth - e.clientWidth body.removeChild(e); return _scrollbarWidth; } 以上就是本文的全部內容,,希望能對您有所幫助:)另外本文第二部分提供的代碼,是根據個人思考和經驗總結出的一些方法,,在兼容性方面可能還有未考慮到的地方,,如果您有遇到其它不兼容的情況或者有更好的代碼,還請不吝賜教,,歡迎您的指導,。 謝謝閱讀:) |
|
來自: 昵稱10504424 > 《工作》