Dans le développement Web, il est inévitable de rencontrer le problème du calcul de la taille et de la position des éléments. La façon de résoudre de tels problèmes consiste à utiliser certaines API fournies par DOM combinées à un traitement de compatibilité. Tout le contenu est divisé en environ 3 articles. .pour expliquer. En tant que premier article, cet article présente les attributs DOM liés à la taille fournis par DOM, fournit certaines méthodes de traitement de compatibilité et explique comment utiliser correctement ces attributs en fonction de scénarios courants.
1. Comprendre correctement offsetWidth, clientWidth, scrollWidth et les attributs de hauteur correspondants
En supposant que les barres de défilement horizontales et verticales d'un certain élément sont déplacées jusqu'à la fin, les plages correspondantes de offsetWidth, clientWidth, scrollWidth et d'autres attributs sont telles qu'indiquées dans la figure ci-dessous :
1) offsetWidth et offsetHeight correspondent à la largeur et à la hauteur du modèle de boîte. Ces deux valeurssont cohérentes avec les tailles que nous voyons lorsque nous utilisons chrome pour inspecter des éléments :
2) scrollWidth, qui correspond à scrollHeight, est la largeur et la hauteur de la zone de défilement, mais n'inclut pas la largeur de la barre de défilement ! La zone de défilement comprend le remplissage et le contenu.
3) clientWidth et clientHeight correspondent à la largeur et à la hauteur de la zone du modèle de boîte après suppression de la bordure, à l'exclusion de la largeur de la barre de défilement.
4) Tout élément DOM peut rapidement obtenir offsetWidth, clientWidth, scrollWidh et les attributs de hauteur associés via l'API suivante :
//domE est un objet DOM Html Element
domE.scrollWidth
domE.scrollHeight
domE.clientWidth
domE.clientHeight
domE.offsetWidth
domE.offsetHeight
//domE est un objet DOM Html Element
domE.scrollWidth
domE.scrollHeight
domE.clientWidth
domE.clientHeight
domE.offsetWidth
domE.offsetHeight
5) Ces attributs ne présentent quasiment aucun problème de compatibilité sur les navigateurs modernes, notamment PC et mobiles, et peuvent être utilisés en toute confiance. Si vous souhaitez connaître les règles détaillées de compatibilité, vous pouvez vous référer aux 2 articles suivants :
Compatibilité DOM W3C – Vue du modèle d'objet CSS
Compilation et introduction du mode d'affichage cssom cssom-view-module
Les attributs liés ci-dessus des éléments HTML ordinaires, des éléments racine HTML et des éléments de corps sont testés un par un afin de vérifier les conclusions précédentes et de résumer certaines techniques d'expérience qui peuvent être directement utilisées dans le processus de codage réel. La raison pour laquelle nous devons faire la distinction entre les éléments html ordinaires, les éléments racine html et les éléments body est due à la théorie précédente. Il y aura une certaine bizarrerie dans les éléments racine html et les éléments body, qui doivent être traités avec précaution.
Remarque :
1. Afin de réduire la longueur, le code publié pour le test n'est pas le code complet, mais cela n'affecte pas la référence d'apprentissage. De plus, les résultats des tests donnés dans l'article sont tous obtenus en exécutant sous chrome. (version : 45.0). Dans les résultats des tests S'il y a des différences, les résultats des tests pour IE9, IE10, IE11, Firefox (version : 42.0) et Opera (version : 34.0) seront également indiqués. ils seront expliqués dans les résultats des tests IE8 et ci-dessous ne seront pas pris en compte.
2. Safari n'est pas testé en raison des limitations de l'appareil. De plus, il est identique au noyau Chrome et la fiabilité du support standard n'est pas très différente.
3. Les anciennes versions de Chrome, Firefox et Opera ne peuvent pas être testées en raison des limitations des appareils. Cependant, compte tenu de la prise en charge des normes par le navigateur, ces trois navigateurs ont commencé à prendre en charge les normes du W3C dans les toutes premières versions. relativement réguliers et ces navigateurs sont mis à jour très rapidement. Les versions courantes de ces navigateurs sur le marché sont également relativement nouvelles.
4. Étant donné que IE8 et versions antérieures ne sont pas pris en compte et que HTML utilise désormais HTML5, donc document.compatMode = 'BackCompat' n'est pas pris en compte. Cependant, bien que le mode BackCompat soit introduit par les navigateurs IE6, document.compatMode = 'BackCompat' existe également pour chrome, firefox, etc. Par exemple, la page web suivante, vous l'ouvrez avec chrome et imprimez document.compatMode dans la console, vous constatera que sa valeur est également BackCompat (la raison est liée au fait que la page utilise le dtd de html4.0. Si vous le remplacez par le dtd de html4.01, cette situation ne se produira pas dans Chrome et Firefox) :
http://samples.msdn.microsoft.com/workshop/samples/author/dhtml/refs/compatModeCompat.htm
Pour plus de connaissances sur compatMode, vous pouvez apprendre des ressources suivantes :
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. Vérifiez les attributs offsetWidth, clientWidth, scrollWidth et les attributs de hauteur associés des éléments HTML ordinaires (éléments non-body et racine HTML) :
<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>
Dans cet exemple, l'élément box a une largeur et une hauteur de 400*300, un padding de 20px et une bordure de 10px Le modèle de box correspondant sous chrome :
résultat de l'exécution js :
À partir du modèle box et des résultats d'exécution de js, nous pouvons savoir :
1) offsetWidth et offsetHeight sont exactement les mêmes que la taille observée lors de l'inspection des éléments en chrome
;2) clientWidth et clientHeight sont respectivement égaux à offsetWidth et offsetHeight moins la bordure correspondante (un total de 20px haut et bas, un total de 20px gauche et droite) et la largeur de la barre de défilement (la largeur de la barre de défilement sous chrome est de 17px );
3) Pour scrollWidth, aucun débordement horizontal ne se produit, et en raison du débordement : scroll, scrollWidth est le même que clientWidth, mais n'inclut pas la largeur de la barre de défilement, ce qui vérifie également la conclusion proposée précédemment
;4) Pour scrollHeight, dans cet exemple, il est en fait égal au remplissage supérieur et inférieur (40px au total) offsetHeight(1370px) de div.box-2, div.box-2 :
5) Il y a un autre CSS à noter dans le test ci-dessus, qui est box-sizing. Dans le code ci-dessus, box-sizing est défini sur content-box. Si vous le remplacez par border-box, le résultat sera. similaire, car offsetWidth et clientWidth sont toujours La zone correspondant à scrollWidth ne changera pas.
6) Les résultats des autres navigateurs sont cohérents avec les conclusions 1 à 5.
Test 2. Vérifiez les attributs de largeur et de hauteur de défilement du client de décalage pertinents de l'élément racine HTML et de l'élément corps :
<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>
이 예에서는 본문 아래에 4개의 상자 요소가 있습니다(총 높이는 360 * 4 = 1440px). 본문의 너비는 가변적이며 본문에도 10px 테두리가 있습니다.
이 결과에서 볼 수 있듯이:
1) body 요소의 테두리가 10px이므로 clientWidth는 offsetWidth보다 20px 작습니다. 이는 위에서 언급한 이론과 일치하지만 놀라운 점은 body의 scrollWidth/scrollHeight가 실제로는 offsetWidth/offsetHeight, scrollWidth /scrollHeight는 요소의 스크롤 영역의 너비와 높이입니다. 위에 제공된 범위 다이어그램에 따르면 본문의 scrollWidth/scrollHeight는 해당 offsetWidth/offsetHeight보다 작아야 합니다.
2) docE의 scrollWidth 및 scrollHeight는 body 요소의 offsetWidth 및 offsetHeight와 같아야 합니다. 실행 결과로 볼 때 이는 일관되지만 docE의 clientWidth는 실제로 해당 offsetWidth와 같습니다. 다이어그램에서 docE의 clientWidth는 offsetWidth에서 스크롤 막대의 너비를 뺀 값과 같아야 합니다.
다른 브라우저의 실행 결과도 크롬과 많이 다릅니다.
IE11:
1) IE11의 body 요소는 크롬의 body 요소와 같은 문제가 없습니다
2) IE11의 html 루트 요소도 크롬과 비슷한 문제가 있습니다
IE10, IE9:
1) IE10, 9의 body 요소는 크롬의 body 요소와 같은 문제가 없습니다
2) IE10 및 9의 html 루트 요소에는 크롬과 유사한 문제가 없습니다
firefox: 실행 결과는 IE11과 일치합니다.
opera: 실행 결과는 Chrome의 결과와 일치합니다. 내 버전의 Opera가 Chrome과 동일한 웹킷 커널을 사용하기 때문일 수 있습니다.
IE9과 IE10이 가장 일반적인 것 같습니다. 오랫동안 온라인으로 검색했지만 이러한 차이점을 설명할 관련 정보를 찾지 못했습니다. 이러한 문제에 대한 몇 가지 설명을 추측해 보세요.
1) 우선 웹페이지 전체를 스크롤하는 것은 일반적인 html 요소의 스크롤과 다릅니다. 일반 html 요소 자체가 스크롤 객체이지만 웹페이지의 경우 스크롤 객체가 반드시 html 루트 요소는 아닙니다. 또는 신체 요소. 왜냐하면 본문 내용이 비어 있으면 본문의 높이가 0이고, html 루트 요소의 높이도 0이기 때문입니다. 이때 html이나 본문에 Overflow: Scroll CSS를 추가하면 스크롤이 되는 것을 볼 수 있습니다. bar는 여전히 브라우저 창 오른쪽에 나타나므로 웹페이지 전체 스크롤의 경우 이론적으로 스크롤 개체는 html 요소나 body 요소가 아닌 창이어야 합니다. 그러나 테스트한 브라우저에서는 그렇지 않습니다.
IE10 및 IE9의 경우 스크롤 개체는 html 루트 요소이므로 html 루트 요소의 오프셋에는 스크롤 막대의 너비가 포함됩니다.
다른 브라우저의 경우 스크롤 개체는 창이므로 html 루트 요소의 오프셋에는 스크롤 막대의 너비가 포함되지 않습니다.
2) 둘째, 일반 요소가 스크롤될 때 스크롤되는 콘텐츠 = 콘텐츠 영역 및 패딩 영역 웹페이지 전체가 스크롤될 때 스크롤되는 콘텐츠는 html 루트 요소여야 합니다! 그러나 브라우저가 테스트한 한 실제로는 그렇지 않습니다.
IE9, IE10, IE11 및 Firefox의 경우 스크롤 영역은 html 루트 요소이므로 documentElement의 scrollWidth 및 scrollHeight는 항상 웹 페이지의 전체 스크롤 영역 크기를 나타냅니다!
Chrome 및 Opera의 경우 스크롤 개체는 본문 요소이므로 본문의 scrollWidth 및 scrollHeight는 항상 웹페이지의 전체 스크롤 영역 크기를 나타냅니다.
3) 셋째, 브라우저는 항상 documentElement.clientWidth 및 documentElement.clientHeight를 스크롤 막대를 제외한 웹페이지의 보이는 영역의 크기로 설명하는데, 이는 웹페이지의 내용과는 아무런 관련이 없습니다!
위의 추론은 타당하지 않습니다. 스크롤 객체와 스크롤 영역을 예로 들어보겠습니다. js를 사용하여 크롬의 특정 위치로 페이지를 스크롤하려면 document를 사용하지 않고 window.scrollTo를 사용해야 합니다. body.scrollTop = xxx를 처리하고 document.documentElement.scrollTop 설정이 유효하지 않습니다. 이는 크롬의 전체 스크롤 영역이 본문의 스크롤 영역에 의해 결정되고 js를 사용하려는 경우를 나타냅니다. IE11 및 Firefox에서 페이지를 특정 위치로 스크롤하려면 window.scrollTo를 사용하지 않고 document.documentElement.scrollTop = xxx를 사용해야 합니다. document.body.scrollTop 설정은 유효하지 않으며 이는 IE11 및 Firefox의 전체 스크롤 영역을 나타냅니다. HTML 루트 요소의 스크롤 영역에 따라 결정됩니다.
2. JS를 사용하여 DOM 개체의 크기를 정확하게 가져옵니다
일반적인 시나리오는 다음과 같습니다.
1) 스크롤바를 제외한 전체 웹페이지의 보이는 영역의 크기를 구합니다2) 보이지 않는 스크롤 영역을 포함한 전체 웹페이지의 크기를 가져옵니다
3) 일반 HTML 요소의 크기 가져오기
4) 요소 또는 웹 페이지에 스크롤 막대를 표시할지 결정
5) 스크롤 막대의 너비 계산
下面针对这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; }
以上就是本文的全部内容,希望能对您有所帮助:)另外本文第二部分提供的代码,是根据个人思考和经验总结出的一些方法,在兼容性方面可能还有未考虑到的地方, 如果您有遇到其它不兼容的情况或者有更好的代码,还请不吝赐教 ,欢迎您的指导。