Є багато властивостей JavaScript, які дозволяють нам читати інформацію про ширину, висоту елемента та інші геометричні характеристики.
Ми часто потребуємо їх під час переміщення або позиціонування елементів в JavaScript.
Зразок елемента
Як зразок елемента для демонстрації властивостей ми використаємо наведений нижче:
<div id="example">
...Text...
</div>
<style>
#example {
width: 300px;
height: 200px;
border: 25px solid #E8C48F;
padding: 20px;
overflow: auto;
}
</style>
Він має межі, відступи та прокручування. Повний набір функцій. Тут немає полів (margins), оскільки вони не є частиною самого елемента, і для них немає спеціальних властивостей.
Елемент виглядає так:
You can open the document in the sandbox.
На малюнку вище показаний найскладніший випадок, коли елемент має смугу прокрутки. Деякі браузери (не всі) резервують місце для нього, беручи його з вмісту (позначеного вище як “ширина вмісту”).
Таким чином, без смуги прокрутки ширина вмісту становила б 300px, але якщо ширина смуги прокрутки 16px (ширина може відрізнятися залежно від пристрою та браузера), то залишається лише 300 - 16 = 284px
, і ми повинні це враховувати. Ось чому приклади з цього розділу припускають наявність смуги прокрутки. Без неї деякі розрахунки простіші.
padding-bottom
може бути заповнена текстомЗазвичай на наших ілюстраціях відступи відображаються порожніми, але якщо в елементі забагато тексту і він переповнюється, то браузери показують “випадаючий” текст в області padding-bottom
, і це нормально.
Геометрія
Ось загальна картина з властивостями геометрії:
Значення цих властивостей технічно є числами, але ці числа є “пікселями”, тому це вимірювання у пікселях.
Давайте дослідимо властивості, починаючи із зовнішніх.
offsetParent, offsetLeft/Top
Ці властивості рідко потрібні, але все ж це “найбільш зовнішні” геометричні властивості, тому й почнемо з них
Найближчий предок – це offsetParent
, який браузер використовує для обчислення координат під час візуалізації.
Це найближчий предок, який є одним із таких:
- CSS-позиціонування (
position
isabsolute
,relative
,fixed
абоsticky
), або <td>
,<th>
, або<table>
, або<body>
.
Властивості offsetLeft/offsetTop
надають координати x/y відносно верхнього лівого кута offsetParent
.
У наведеному нижче прикладі внутрішній <div>
має <main>
як offsetParent
і offsetLeft/offsetTop
зсувається від свого верхнього лівого кута (180
):
<main style="position: relative" id="main">
<article>
<div id="example" style="position: absolute; left: 180px; top: 180px">...</div>
</article>
</main>
<script>
alert(example.offsetParent.id); // main
alert(example.offsetLeft); // 180 (note: a number, not a string "180px")
alert(example.offsetTop); // 180
</script>
Існує декілька випадків, коли offsetParent
дорівнює null
:
- Для елементів, що не відображаються (
display:none
або ті, що поза документом). - Для
<body>
та<html>
. - Для елементів з
position:fixed
.
offsetWidth/Height
Тепер перейдемо до самого елемента.
Ці дві властивості є найпростішими. Вони забезпечують “зовнішню” ширину/висоту елемента. Або, іншими словами, його повний розмір, включаючи рамки(border).
Для нашого зразка елемента:
offsetWidth = 390
– зовнішня ширина, яку можна обчислити як внутрішню CSS-ширину (300px
) плюс відступи (2 * 20px
) і рамки (2 * 25px
).offsetHeight = 290
– зовнішня висота.
Геометричні властивості обчислюються лише для відображених елементів.
Якщо елемент (або будь-який із його предків) має display:none
або його немає в документі, тоді всі геометричні властивості дорівнюють нулю (або null
для offsetParent
).
Наприклад, offsetParent
дорівнює null
, а offsetWidth
, offsetHeight
дорівнюють 0
, якщо ми створили елемент, але ще не вставили його в документ, або він (або його предок) має display:none
.
Ми можемо використати це, щоб перевірити, чи приховано елемент, наприклад:
function isHidden(elem) {
return !elem.offsetWidth && !elem.offsetHeight;
}
Зверніть увагу, що такий isHidden
повертає true
для елементів, які є на екрані, але мають нульовий розмір.
clientTop/Left
Всередині елемента ми маємо рамки.
Для їх вимірювання існують властивості clientTop
і clientLeft
.
У нашому прикладі:
clientLeft = 25
– ширина рамки ліворучclientTop = 25
– ширина рамки зверху
…Але якщо бути точним – ці властивості не ширина/висота рамки, а радше відносні координати внутрішньої сторони від зовнішньої.
Яка різниця?
Стає зрозуміло, коли документ написаний справа наліво (операційна система арабською або івритом). Тоді смуга прокрутки розташована не праворуч, а ліворуч, а тому clientLeft також включає ширину смуги прокрутки.
У цьому випадку clientLeft
буде не 25
, бо додасться ширина смуги прокрутки 25 + 16 = 41
.
Ось приклад на івриті:
clientWidth/Height
Ці властивості забезпечують розмір області всередині меж елемента.
Вони включають ширину вмісту разом із відступами, але без смуги прокрутки:
На зображенні вище давайте спочатку розглянемо clientHeight
.
Немає горизонтальної смуги прокрутки, тому це точно сума того, що знаходиться всередині меж елемента: CSS-висота 200px
плюс верхній і нижній відступи (2 * 20px
) загалом 240px
.
Тепер clientWidth
– тут ширина вмісту не 300px
, а 284px
, тому що 16px
займає смуга прокрутки. Таким чином, сума становить 284px
плюс відступи ліворуч і праворуч, разом 324px
.
Якщо немає відступів, тоді clientWidth/Height
це саме область вмісту, всередині рамок і смуги прокрутки (якщо вона є).
Отже, коли немає відступів, ми можемо використовувати clientWidth/clientHeight
, щоб отримати розмір області вмісту.
scrollWidth/Height
Ці властивості схожі на clientWidth/clientHeight
, але вони також включають прокручені (приховані) частини:
На зображенні вище:
scrollHeight = 723
– повна внутрішня висота області вмісту, включаючи прокручені частини.scrollWidth = 324
– це повна внутрішня ширина, тут у нас немає горизонтальної прокрутки, тому вона дорівнюєclientWidth
.
Ми можемо використовувати ці властивості, щоб розширити елемент на всю ширину/висоту.
Як тут:
// expand the element to the full content height
element.style.height = `${element.scrollHeight}px`;
Натисніть кнопку, щоб розгорнути елемент:
scrollLeft/scrollTop
Властивості scrollLeft/scrollTop
це ширина/висота прихованої, прокрученої частини елемента.
На малюнку нижче ми бачимо scrollHeight
та scrollTop
для блоку з вертикальною прокруткою.
Іншими словами, scrollTop
означає “скільки прокручено вгору”.
scrollLeft/scrollTop
можна змінитиБільшість геометричних властивостей тут доступні лише для зчитування, але scrollLeft/scrollTop
можна змінити, і браузер прокрутить елемент відповідно до змін.
Якщо клацнути елемент нижче, буде виконано код elem.scrollTop += 10
. Це змусить вміст елемента прокрутитися на 10px
вниз.
Me
1
2
3
4
5
6
7
8
9
Встановлення scrollTop
на 0
або навпаки велике значення, наприклад 1e9
змусить елемент прокрутитися до самого верху/низу відповідно.
Не беріть ширину/висоту з CSS
Ми щойно розглянули геометричні властивості елементів DOM, які можна використовувати для отримання ширини, висоти та обчислення відстані.
Але, як ми знаємо з розділу Стилі та класи, ми можемо зчитати CSS висоту та ширину за допомогоюgetComputedStyle
.
То чому б не прочитати ширину елемента за допомогою getComputedStyle
, як тут?
let elem = document.body;
alert( getComputedStyle(elem).width ); // show CSS width for elem
Чому ми повинні замість цього використовувати геометричні властивості? Є дві причини:
-
По-перше, CSS
width/height
залежить від іншої властивості:box-sizing
яка визначає “що таке” ширина та висота CSS. Зміни вbox-sizing
для CSS можуть зламати такий JavaScript. -
По-друге, CSS
width/height
може бутиauto
, наприклад для вбудованого елемента:<span id="elem">Hello!</span> <script> alert( getComputedStyle(elem).width ); // auto </script>
З точки зору CSS,
width:auto
є абсолютно нормальним, але в JavaScript нам потрібен точний розмір уpx
який ми можемо використовувати в обчисленнях. Отже, тут CSS ширина марна.
І є ще одна причина: смуга прокрутки. Іноді код, який добре працює без смуги прокрутки, починає працювати з помилками, оскільки смуга прокрутки займає простір у вмісті в деяких браузерах. Отже, реальна ширина, доступна для вмісту, менша за ширину CSS. І clientWidth/clientHeight
враховують це.
…Але з getComputedStyle(elem).width
ситуація інша. Деякі браузери (наприклад, Chrome) повертають реальну внутрішню ширину без смуги прокрутки, а деякі з них (наприклад, Firefox) – CSS ширину (ігнорує прокрутку). Такі міжбраузерні відмінності є причиною не використовувати getComputedStyle
, а радше покладатися на геометричні властивості.
Якщо ваш браузер резервує простір для смуги прокрутки (більшість браузерів для Windows це роблять), ви можете перевірити це нижче.
Елемент із текстом має CSS width:300px
.
На комп’ютері в ОС Windows, Firefox, Chrome, Edge резервується місце для смуги прокрутки. Але Firefox показує 300px
, а Chrome і Edge – менше. Це тому, що Firefox повертає ширину CSS, а інші браузери повертають “реальну” ширину.
Зверніть увагу, що описана різниця стосується лише читання getComputedStyle(...).width
з JavaScript, візуально все правильно.
Підсумки
Елементи мають наступні геометричні властивості:
offsetParent
– це найближчий позиціонований предок абоtd
,th
,table
,body
.offsetLeft/offsetTop
– координати відносно лівого верхнього кутуoffsetParent
.offsetWidth/offsetHeight
– “зовнішня” ширина/висота елемента, включаючи рамки.clientLeft/clientTop
– відстані від верхнього лівого зовнішнього кута до верхнього лівого внутрішнього (вміст + відступ) кута. Для ОС, орієнтованої зліва направо, це завжди ширина лівої/верхньої рамок. Для ОС, орієнтованої справа наліво, вертикальна смуга прокрутки розташована ліворуч, томуclientLeft
також включає її ширину.clientWidth/clientHeight
– ширина/висота вмісту, включаючи відступи, але без смуги прокрутки.scrollWidth/scrollHeight
– ширина/висота вмісту, як іclientWidth/clientHeight
, але також включає прокручену невидиму частину елемента.scrollLeft/scrollTop
– ширина/висота прокрученої верхньої частини елемента, починаючи з його верхнього лівого кута.
Усі властивості доступні лише для читання, за винятком scrollLeft/scrollTop
які змушують браузер прокручувати елемент у разі зміни.
Коментарі
<code>
, для кількох рядків – обгорніть їх тегом<pre>
, для понад 10 рядків – використовуйте пісочницю (plnkr, jsbin, codepen…)