Home > Article > Web Front-end > JavaScript position and size (1) Correct understanding and use of DOM attributes related to size_javascript skills
在web開發中,不可避免遇到要計算元素大小以及位置的問題,解決這類問題的方法是利用DOM提供的一些API結合兼容性處理來,所有內容大概分3篇左右的文章的來說明。本文作為第一篇,介紹DOM提供的與尺寸大小相關的DOM屬性,提供一些相容性處理的方法,並結合常見的場景說明如何正確運用這些屬性。
1. 正確理解offsetWidth、clientWidth、scrollWidth及對應的height屬性
假設某一個元素的橫縱向捲軸都拖曳到最末端,則offsetWidth、clientWidth、scrollWidth等屬性對應的範圍如下圖所示:
1)offsetWidth ,offsetHeight對應的是盒子模型的寬度和高度,這兩個值跟我們使用chrome審查元素時看到的尺寸一致:
2)scrollWidth,與scrollHeight對應的是滾動區域的寬度和高度 , 但不包含滾動條的寬度!滾動區域由padding和content組成。
3)clientWidth,clientHeight對應的是盒子模型除去邊框後的那部分區域的寬度和高度,不包含滾動條的寬度。
4)任何一個DOM元素,都可以透過以下api快速得到offsetWidth,clientWidth,scrollWidh及相關的height屬性:
//domE為一個DOM Html Element物件
domE.scrollWidth
domE.scrollHeight
domE.clientWidth
domE.clientHeight
domE.offsetWidth
domE.offsetHeight
//domE為一個DOM Html Element物件
domE.scrollWidth
domE.scrollHeight
domE.clientWidth
domE.clientHeight
domE.offsetWidth
domE.offsetHeight
5) 這些屬性在現代瀏覽器包括pc和mobile上幾乎沒有相容性問題,可以放心使用 。如果你想了解詳細的相容性規則,可以參考下面的2篇文章:
W3C DOM Compatibility – CSS Object Model View
cssom視圖模式cssom-view-module相關整理與介紹
下面針對普通html元素,html根元素和body元素的以上相關屬性一一測試,以便驗證前面的結論,總結一些可在實際編碼過程中直接使用的經驗技巧。之所以要區分普通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的標準都是比較規矩的,加上這些瀏覽器更新換代的速度較快,現在市面上這些瀏覽器主流的版本也都是較新的。
4、由於不考慮IE8以下,同時html現在都用html5,所以document.compatMode = ‘BackCompat' 的情況不考慮。不過儘管BackCompat模式是IE6類的瀏覽器引出的,但是對於chrome,firefox等也存在document.compatMode = 'BackCompat' 的情況,比如下面的這個網頁,你用chrome打開,並且在console中打印document.compatMode ,你會發現它的值也是BackCompat(原因跟該頁面用的是html4.0的dtd有關,如果換成html4.01的dtd就不會在chrome和firefox裡出現該情況了):
http://samples.msdn.microsoft.com/workshop/samples/author/dhtml/refs/compatModeCompat.htm
更多關於compatMode的知識,你可以透過下面的幾個資源學習:
https://developer.mozilla.org/zh-CN/docs/Web/API/Document/compatMode
https://msdn.microsoft.com/en-us/library/ms533687(VS.85).aspx
http://www.cnblogs.com/uedt/archive/2010/09/21/1832402.html
Test 1. Verify the offsetWidth, clientWidth, scrollWidth and related height attributes of ordinary html elements (non-body and html root elements):
<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> <styletype="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> <divclass="box"> <divclass="box-2">...</div> </div> </body> <scripttype="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>
In this example, the box element has a width and height of 400*300, a padding of 20px and a border of 10px. The corresponding box model under chrome:
js execution result:
From the box model and js execution results, we can know:
1) offsetWidth and offsetHeight are exactly the same as the size seen when inspecting elements in chrome;
2) clientWidth and clientHeight are respectively equal to offsetWidth and offsetHeight minus the corresponding border (a total of 20px up and down, a total of 20px left and right) and the scroll bar width (the scroll bar width under chrome is 17px);
3) For scrollWidth, no horizontal overflow occurs, and due to overflow: scroll, scrollWidth is the same as clientWidth, but does not include the width of the scroll bar, which also verifies the conclusion proposed earlier;
4) For scrollHeight, in this example, it is actually equal to the top and bottom padding (40px in total) + offsetHeight(1370px) of div.box-2, div.box-2:
5) There is another CSS worth noting in the above test, which is box-sizing. In the above code, box-sizing is set to content-box. If you change it to border-box, the result will be similar, because offsetWidth and clientWidth are still The area corresponding to scrollWidth will not change.
6) The results of other browsers are consistent with the conclusions 1-5.
Test 2. Verify the relevant offset client scroll width and height attributes of the html root element and body element:
<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> <styletype="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> <divclass="box"> <divclass="box-2">...</div> </div> <divclass="box"> <divclass="box-2">...</div> </div> <divclass="box"> <divclass="box-2">...</div> </div> <divclass="box"> <divclass="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>
In this example, there are 4 box elements under the body (total height is 360 * 4 = 1440px). The width of the body is adaptive, and the body also has a 10px border. The running results are as follows:
As you can see from this result:
1) Due to the 10px border of the body element, the clientWidth is 20px less than the offsetWidth. This is consistent with the theory mentioned above, but what is incredible is that the scrollWidth/scrollHeight of the body is actually equal to its offsetWidth/offsetHeight, scrollWidth /scrollHeight is the width and height of the scroll area of the element. According to the range diagram given above, the scrollWidth/scrollHeight of the body should be smaller than its offsetWidth/offsetHeight;
2) The scrollWidth and scrollHeight of docE should be equal to the offsetWidth and offsetHeight of the body element. Judging from the running results, this is consistent, but the clientWidth of docE is actually equal to its offsetWidth. According to the range diagram, the clientWidth of docE should be It is equal to offsetWidth minus the width of the scroll bar.
The running results of other browsers are also quite different from chrome:
IE11:
1) The body element under IE11 does not have the problem of the body element under chrome
2) The html root element under IE11 also has a similar problem to chrome
IE10, IE9:
1) The body element under IE10 and 9 does not have the problem of the body element under chrome
2) The html root element under IE10 and 9 does not have similar problems as chrome
firefox: The running results are consistent with IE11.
opera: The running result is consistent with that of chrome. It may be because my version of opera uses the same webkit kernel as chrome.
It seems that IE9 and IE10 are the most normal. It is a bit difficult to understand. I searched online for a long time and found no relevant information to explain these differences. In the end, I could only make bold assumptions and guess a few explanations. Reasons for these problems:
1) First of all, the scrolling of the entire web page is different from the scrolling of ordinary html elements. Ordinary html elements themselves are the scroll objects, but for web pages, the scroll objects are not necessarily the html root element or body element. Because when the body content is empty, the height of the body is 0, and the height of the html root element is also 0. If you add overflow: scroll css to the html or body at this time, you will see that the scroll bar still appears on the right side of the browser window. The bottom edge, so for the overall scrolling of the web page, in theory, the scrolling object should be the window, not the html element or body element! But this is not the case, as far as the browsers tested:
For IE10 and IE9, its scrolling object is the html root element, so the offset of their html root element will include the width of the scroll bar;
For other browsers, the scroll object is window, so the offset of their html root element does not include the width of the scroll bar.
2) Second, when a normal element scrolls, the scrolling content = its content area + its padding area. When the web page scrolls as a whole, the scrolling content should be the html root element! But this is not actually the case, as far as the browsers tested:
For IE9, IE10, IE11, and Firefox, their scrolling area is the html root element, so the scrollWidth and scrollHeight of their documentElement always represent the overall scrolling area size of the web page!
For chrome and opera, their scrolling object is the body element, so their body's scrollWidth and scrollHeight always represent the overall scrolling area size of the web page!
3) Third, the browser always describes documentElement.clientWidth and documentElement.clientHeight as the size of the visible area of the webpage excluding the scroll bar, which has nothing to do with the content of the webpage!
The above inferences are not unreasonable. Take the scrolling object and scrolling area as an example: If you want to use js to scroll the page to a certain position in chrome, you must use window.scrollTo without using it. document.body.scrollTop = xxx to process, and setting document.documentElement.scrollTop is invalid, indicating that the overall scrolling area of chrome is determined by the scrolling area of the body; and if you want to use js to scroll the page to a certain position under IE11 and Firefox, Without using window.scrollTo, document.documentElement.scrollTop = xxx must be used. Setting document.body.scrollTop is invalid, indicating that the overall scrolling area of IE11 and Firefox is determined by the scrolling area of the HTML root element.
2. Use JS to accurately obtain the size of the DOM object
Common scenarios include:
1) Get the size of the visible area of the entire web page, excluding scroll bars
2) Get the size of the entire web page, including invisible scrolling area
3) Get the size of an ordinary html element
4) Determine whether scroll bars appear on elements or web pages
5) Calculate the width of the scroll bar
下面针对这5个场景一一说明,以下代码均 不考虑IE8及以下,不考虑html4 ,另外请注意viewport的设置,要保证在移动设备上visual viewport与layout viewport重合。
1)如何获取整个网页的可视区域的大小,不包括滚动条
document.documentElement.clientWidth; document.documentElement.clientHeight; document.documentElement.clientWidth; document.documentElement.clientHeight;
2)如何获取整个网页的大小,包括不可见的滚动区域
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"] ); } 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分别用来获取网页包括滚动条的可视区域的宽高,这也是一个兼容性不错的方法,不过从实际开发情况来看,我们需要不包括滚动条的可视区域更多一些,所以在前面没有单独介绍。另外在之前给出的PPK的博客中也有关于这两个属性的兼容性测试,可以去了解。
3)如何获取一个普通html元素的大小
简单方法:
docE.offsetWidth; docE.offsetHeight; 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; } 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) }; } 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' & !(eleminstanceof(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; } 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; }
以上就是本文的全部内容,希望能对您有所帮助:)另外本文第二部分提供的代码,是根据个人思考和经验总结出的一些方法,在兼容性方面可能还有未考虑到的地方, 如果您有遇到其它不兼容的情况或者有更好的代码,还请不吝赐教 ,欢迎您的指导。