Є багато властивостей JavaScript, які дозволяють нам читати інформацію про ширину, висоту елемента та інші геометричні характеристики.
Ми часто потребуємо їх під час переміщення або позиціювання елементів в JavaScript.
Приклад елемента
Як приклад елемента для демонстрації властивостей ми використаємо наведений нижче:
<div id="example">
...Текст...
</div>
<style>
#example {
width: 300px;
height: 200px;
border: 25px solid #E8C48F;
padding: 20px;
overflow: auto;
}
</style>
Він має рамки, відступи та прокручування. Повний набір функцій. Тут немає зовнішніх відступів (margins), оскільки вони не є частиною самого елемента, і для них немає спеціальних властивостей.
Елемент виглядає ось так:
Ви можете відкрити документ у пісочниці.
На малюнку вище показаний найскладніший випадок, коли елемент має смугу прокрутки. Деякі браузери (не всі) резервують місце для нього, беручи його з вмісту (позначеного вище як “content width”).
Таким чином, без смуги прокрутки ширина вмісту становила б 300px, але якщо ширина смуги прокрутки 16px (ширина може відрізнятися залежно від пристрою та браузера), то залишається лише 300 - 16 = 284px, і ми повинні це враховувати. Ось чому приклади з цього розділу надані зі смугою прокрутки. Без неї деякі розрахунки будуть простіші.
padding-bottom (нижній внутрішній відступ) може бути заповнена текстомЗазвичай на наших ілюстраціях відступи відображаються порожніми, але якщо в елементі забагато тексту і він переповнюється, то браузери показують “випадаючий” текст в області padding-bottom, і це нормально.
Геометрія
Ось ілюстрація з усіма геометричними властивостями:
Значення цих властивостей технічно є числами, але ці числа є “пікселями”, тому це вимірювання у пікселях.
Почнемо досліджувати, починаючи зовні елемента.
offsetParent, offsetLeft/Top
Ці властивості рідко потрібні, але все ж це “найбільш зовнішні” геометричні властивості, тому й почнемо з них.
У властивості offsetParent знаходиться предок елементу, який браузер використовує для обчислення координат під час візуалізації.
Тобто найближчий предок, який задовольняє наступним умовам:
- Є CSS-позиціонованим (CSS-властивість
positionєabsolute,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 (зверніть увагу: число, а не рядок "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 для елементів, які в принципі показуються, але їх розміри дорівнюють нулю (наприклад, порожні <div>).
clientTop/Left
Підемо далі. Всередині елемента у нас є рамки (border).
Для їх вимірювання існують геометричні властивості clientTop та clientLeft.
Для нашого елемента:
clientLeft = 25– ширина лівої рамкиclientTop = 25– ширина верхньої рамки
…Але якщо бути точним – ці властивості не ширина/висота рамки, а відступи внутрішньої частини елемента від зовнішньої.
Яка різниця?
Вона виникає, коли документ написаний справа наліво (операційна система арабською або івритом). Тоді смуга прокрутки розташована не праворуч, а ліворуч, а тому clientLeft також включає ширину смуги прокрутки.
У цьому випадку clientLeft буде не 25, бо додасться ширина смуги прокрутки: 25 + 16 = 41.
Ось приклад на івриті:
clientWidth/Height
Ці властивості забезпечують розмір області всередині рамок елемента.
Вони включають ширину вмісту разом із внутрішніми відступами padding, але без смуги прокрутки:
На зображенні вище давайте спочатку розглянемо висоту clientHeight.
Немає горизонтальної смуги прокрутки, тому це точно сума того, що знаходиться всередині меж елемента: CSS-висота 200px плюс верхній і нижній відступи (2 * 20px) загалом 240px.
Тепер clientWidth – ширина вмісту тут дорівнює не 300px, а 284px, тому що 16px займає смуга прокрутки. Таким чином, сума становить 284px плюс відступи ліворуч і праворуч, разом 324px.
Якщо немає відступів padding, то clientWidth/Height в точності рівні розміру області вмісту всередині рамок за вирахуванням смуги прокручування (якщо вона є).
Тому в тих випадках, коли ми точно знаємо, що відступів немає, можна використовувати clientWidth/clientHeight для отримання розмірів внутрішньої області вмісту.
scrollWidth/Height
Ці властивості схожі на clientWidth/clientHeight, але вони також включають прокручену (приховану) частину елемента:
На зображенні вище:
scrollHeight = 723– повна внутрішня висота області вмісту, включаючи прокручену область.scrollWidth = 324– це повна внутрішня ширина, тут у нас немає горизонтальної прокрутки, тому вона дорівнюєclientWidth.
Ми можемо використовувати ці властивості, щоб розширити елемент на всю ширину/висоту.
Як тут:
// розгорнути елемент на всю висоту вмісту
element.style.height = `${element.scrollHeight}px`;
Натисніть кнопку, щоб розгорнути елемент:
scrollLeft/scrollTop
Властивості scrollLeft/scrollTop це ширина/висота прихованої, прокрученої частини елемента.
На малюнку нижче ми бачимо scrollHeight та scrollTop для блоку з вертикальною прокруткою.
Іншими словами, scrollTop означає “скільки прокручено вгору”.
scrollLeft/scrollTop можна змінюватиБільшість геометричних властивостей тут доступні лише для читання, але scrollLeft/scrollTop можна змінювати, і браузер прокрутить елемент відповідно до змін.
Якщо клацнути на елемент нижче, буде виконано код elem.scrollTop += 10. Це змусить вміст елемента прокрутитися на 10px вниз.
На мене
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 ); // показує CSS-ширину elem
Чому ми повинні замість цього використовувати геометричні властивості? Є дві причини:
-
По-перше, CSS
width/heightзалежить від іншої властивості:box-sizingяка визначає “що таке” ширина та висота CSS. Виходить, що змінаbox-sizing, наприклад, для зручнішої верстки, зламає такий JavaScript. -
По-друге, CSS
width/heightможе бутиauto, наприклад для вбудованого елемента:<span id="elem">Привіт!</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– це найближчий CSS-позиціонований предок або найближчий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…)