Размеры и скроллинг элементов в JavaScript

Размеры и скроллинг элементов в JavaScript
Содержание:
  1. Блочная модель CSS
  2. Размеры элемента
  3. Положение элемента
  4. Прокрутка элемента
  5. Разное
  6. Задачи
  7. Комментарии

В этой статье познакомимся с тем, как в JavaScript получить текущие метрики элемента, включая его ширину и высоту.

Блочная модель CSS

Блочная модель в CSS – это набор правил, на основании которых выполняется расчёт размеров элемента на странице. Эту модель необходимо знать, чтобы понимать, какие размеры будет иметь тот или иной блочный элемент на странице:

Блочная модель CSS

Здесь:

  • content – содержимое блока (в этом примере его ширина width равна 800px, а высота height150px);
  • padding – внутренний отступ;
  • border – рамка;
  • margin – внешний отступ.

Кроме этого, элемент может иметь ещё outline (внешнюю границу). Но она не занимает места, не влияет на его размеры и положение.

По умолчанию, когда мы в CSS элементу устанавливаем width и height, они применяются к content:

CSS
.box {
  width: 800px;
  height: 150px;
  padding: 15px 20px;
  border: 3px solid #696a6f;
  margin: 20px 8px;
}

В этом сценарии «внешняя» ширина блока будет 846px (800 + 20 * 2 + 3 * 2), а высота – 186px (150 + 15 * 2 + 3 * 2).

Но в CSS кроме такого поведения (box-sizing: content-box) имеется ещё border-box:

CSS
.box {
  box-sizing: border-box;
  width: 846px;
  height: 186px;
  padding: 15px 20px;
  border: 3px solid #696a6f;
  margin: 20px 8px;
}
Пример, как в CSS, рассчитывается общая ширина и высота элемента, если установить box-sizing значение border-box

При такой настройке мы с помощью width и height уже устанавливаем элементу внешнюю ширину и высоту. А размеры его content области будут уже вычисляться исходя из этих значений.

Кроме этого, размер доступной области content зависит ещё зависит от полос прокрутки. Если полоса прокрутки в браузере на данном устройстве занимает место, то для её отображения будет отбираться соответствующее место у контента.

Размеры элемента

Блочная модель CSS довольно сложна, особенно, когда элемент имеет полосы прокрутки. Поэтому, в JavaScript для описания размеров элемента на странице имеется такое многообразие DOM-свойств:

  • offsetWidth и offsetHeight – «внешняя» ширина и высота элемента (в пикселях), включая рамку;
  • clientLeft и clientTop – толщина левой и верхней границы элемента (если направление текста справа налево, то clientLeft включает в себя ещё ширину вертикальной полосы прокрутки);
  • clientWidth и clientHeight – ширина и высота content, включая padding (без прокрутки);
  • scrollWidth и scrollHeight – ширина и высота content, включая padding и содержимое, которое не помещается на экран и требует прокрутки.
DOM-свойства, описывающие размеры элемента на странице

Перед тем как рассмотреть пример, отметим, что при разработке сайтов очень часто сразу для всех элементов устанавливают border-box:

CSS
*,
*::before,
*::after {
  box-sizing: border-box;
}

Поэтому, изучать размеры элементов будем с учётом этой настройки:

HTML
<style>
  .box-wrapper {
    width: 300px;
    height: 400px;
    padding: 15px;
    margin: 20px;
    overflow: scroll;
  }

  .box-1 {
    height: 100px;
    margin-bottom: 15px;
  }

  .box-2 {
    height: 150px;
    width: 450px;
    margin-bottom: 15px;
  }

  .box-3 {
    height: 450px;
  }
</style>

<div class="box-wrapper">
  <div class="box-1"></div>
  <div class="box-2"></div>
  <div class="box-3"></div>
</div>

На рисунке в правом верхнем углу показаны значения DOM-свойств для .box-wrapper:

Определяем с помощью DOM-свойств размеры элемента на странице

1. Свойства offsetWidth и offsetHeight возвращают «внешние» размеры элемента.

Свойства offsetWidth и offsetHeight для получения внешних размеров элемента

То есть размеры, которые получаются сложением content,padding, ширины соответствующей полосы прокрутки (если она отображается и занимает место) и рамки. Для .box-wrapper:

  • offsetWidth равно CSS-ширине или 300px;
  • offsetHeight равно CSS-высоте height или 400px.

Свойства offsetWidth и offsetHeight возвращают целые значения. Но фактические размеры элемента могут быть дробными. Поэтому, если нужно узнать точные размеры, то можно воспользоваться getBoundingClientRect:

JavaScript
// получаем элемент .box-wrapper
const boxWrapperElem = document.querySelector('.box-wrapper');
// целые «внешние» размеры
const width1 = boxWrapperElem.offsetWidth;
const height1 = boxWrapperElem.offsetHeight;
// точные «внешние» размеры
const boxWrapperClientRect = boxWrapper.getBoundingClientRect();
const width2 = boxWrapperClientRec.width;
const height2 = boxWrapperClientRec.height;

2. Свойства clientLeft и clientTop возвращают соответственно толщину левой и верхней границ. Но, clientLeft также включает ширину вертикальной полосы прокрутки, если направление текста установлено справа налево (поскольку в этом случае полоса отображается слева).

Свойства clientLeft и clientTop для получения отступов от внешней границы рамки до внутренней части элемента

В этом примере:

  • clientLeft равен 15px (толщине левой рамки);
  • clientTop возвращает толщину верхней рамки, которая равна 15px.

3. Свойства clientWidth и clientHeight возвращают размеры области содержимого элемента, включая внутренние отступы (padding), но без учета границ (border) и прокрутки (scroll).

Свойства clientWidth и clientHeight, позволяющие получить размеры области содержимого элемента, включая padding

Свойство clientWidth равно 253px и оно на этом устройстве вычислилось следующим образом:

  • 300px (offsetWidth);
  • -15px (clientLeft или толщина левой рамки);
  • -15px (толщина правой рамки .box-wrapper);
  • -17px (ширина прокрутки на этом устройстве).

Свойство clientHeight равно 353px и оно на этом устройстве вычислилось следующим образом:

  • 400px (offsetHeight);
  • -15px (clientTop или толщина верхней рамки);
  • -15px (толщина нижней рамки);
  • -17px (ширина горизонтальной прокрутки на этом устройстве).

4. Свойства scrollWidth и scrollHeight возвращают размеры области содержимого элемента, включая внутренние отступы (padding) и содержимое, которое не помещается на экране и требует прокрутки.

Свойства scrollWidth и scrollHeight, возвращающие ширину и высоту content, включая padding и содержимое, которое не помещается на экран и требует прокрутки.

Свойство scrollWidth аналогично clientWidth, но учитывает контент .box-wrapper, который не видно. В этом примере scrollWidth равна 480px:

  • 15px (padding-left блока .box-wrapper);
  • 450px (ширина блока .box-2, так как из-за него и возникает прокрутка);
  • 15px (padding-right блока .box-wrapper).

Величина scrollHeight равна 760px. Её можно посчитать следующим образом:

  • 15px (padding-top блока .box-wrapper);
  • 100px (внешняя высота .box-1);
  • 15px (margin-bottom, установленный для .box-1);
  • 150px (внешняя высота .box-2);
  • 15px (margin-bottom, установленный для .box-2);
  • 450px (внешняя высота .box-3);
  • 15px (padding-bottom блока .box-wrapper).

Положение элемента

Кроме размеров элемента, нам также очень важно знать как он расположен на странице. В JavaScript мы можем это получить с помощью DOM-свойств offsetLeft и offsetTop.

Очень важно, эти свойства показывают положение элемента (его смещение) относительно offsetParent.

Например, определим позицию элемента #child на странице относительно его offsetParent (в данном примере им является #parent):

HTML
<div id="parent" style="position: relative; padding: 10px 15px;">
  <div id="child">
</div>

<script>
  const child = document.querySelector('#child');
  console.log(childElem.offsetParent.id); // "parent"
  console.log(childElem.offsetLeft); // 15
  console.log(childElem.offsetTop); // 10
</script>
DOM-свойства, с помощью которых можно как получить положение прокрутки, так и изменить её

Теперь рассмотрим, что такое offsetParent.

offsetParent – это свойство элемента, с помощью которого мы можем получить ближайшего предка с position отличным от static. Другими словами, оно позволяет найти предка относительного которого данный элемент позиционируется.

Поиск предка всегда начинается с родителя, дальше осуществляется переход к родителю его родителя и так далее. Если на очередном шаге элемент отвечает искомому, то он возвращается в качестве результата. В противном случае document.body.

Например, найдём offsetParent для элемента, который помещён в блок, у которого position равен absolute:

HTML
<div id="parent" style="position: absolute;">
  <div id="child"></div>
</div>

<script>
  const childElem = document.querySelector('#child');
  const offsetParentElem = childElem.offsetParent;
  // найдём ближайший родительский элемент с position отличным от static
  console.log(offsetParentElem);
</script>

Кроме этого offsetParent рассматривает в качестве цели не только элементы с position отличным от static, но и <table>, <td> и <th>.

Пример использования:

const tdElem = document.querySelector('#myTd');
const tdOffsetParentElem = tdElem.offsetParent;
// выведем ближайший родительский элемент таблицы с position отличным от static
console.log(tdOffsetParentElem);

const tableElem = document.querySelector('#myTable');
const tableOffsetParentElem = tableElem.offsetParent;
// выведем ближайший родительский элемент с position отличным от static, который может быть как внутри таблицы, так и за ее пределами
console.log(tableOffsetParentElem); 

При этом предком может быть только:

  • позиционированный элемент, т.е. такой, который имеет position отличное от static, т.е. relative, absolute, fixed или sticky;
  • <td>, <th> или <table>;
  • <body>.

offsetParent возвращает null в следующих ситуациях:

  • для <body>;
  • элемент не отображается (он или его предок имеет display:none) или его нет в документе (он создан, но не вставлен);
  • элемент имеет фиксированное позиционирование position:fixed;

Прокрутка элемента

Положение прокрутки определяется с помощью свойств scrollLeft и scrollTop. Они показывают количество пикселей, на которое прокручено содержимое элемента по горизонтали и вертикали.

Свойства scrollLeft и scrollTop в отличие от других доступны не только для чтения, но и для изменения.

scrollLeft и scrollTop - это свойства объекта элемента в JavaScript, которые возвращают текущую позицию прокрутки элемента по горизонтали и вертикали соответственно. Они могут использоваться для определения текущей позиции прокрутки элемента и для управления прокруткой программно. Например, чтобы прокрутить элемент вверх или вниз на определенное количество пикселей, можно установить значение свойства scrollTop равным нужному количеству пикселей.

В отличие от большинства свойств, которые доступны только для чтения, значения scrollLeft и scrollTop можно изменять, и браузер выполнит прокрутку элемента.

HTML
<div class="box-wrapper">
  ...
</div>

<script>
  // получим элемент .box-wrapper
  const boxWrapperElem = document.querySelector('.box-wrapper');
  // выведем в консоль позицию горизонтальной прокрутки
  console.log(boxWrapperElem.scrollLeft);
  // выведем в консоль позицию вертикальной прокрутки
  console.log(boxWrapperElem.scrollTop);
  // установим в качестве прокрутки по x значение 100px
  boxWrapperElem.scrollLeft = 100;
  // прокрутим по у на 50px
  boxWrapperElem.scrollTop += 50;
</script>
DOM-свойства, с помощью которых можно как получить положение прокрутки, так и изменить её

Разное

Для задания элементу ширины и высоты следует использовать стили:

HTML
<div id="box"></div>

<script>
  const $box = document.querySelector('#box');
  // устанавливаем ширину $box
  $box.style.width = '100px';
  // устанавливаем высоту $box
  $box.style.height = '100px';
</script>

Задачи

1. Устранить сдвиг при открытии модального окна

При открытии модального окна мы добавляем к body свойство overflow:hidden.

При этом, если у нас отображается вертикальная полоса прокрутки, которая занимает место, то при обрезании контента происходит смещение контента.

Эту ситуацию необходимо исправить.

Подсказки:

  • необходимо вычислить ширину прокрутки (т.к. в разных браузерах и на разных устройствах она может иметь разную ширину);
  • при открытии модального окна следует добавить к body правый margin со значением которое будет компенсировать ширину прокрутки.

Комментарии: 1

auvarov
auvarov

Примеры в статье и в песочнице различаются.

Отсюда если не заходить в песочницу кажется ничего не понятным....

В песочнице данные точнее (новее).

Например: -15px (толщина правой рамки .box-wrapper); но в статье нет рамки CSS

Чтобы попасть в песочницу нажимайте кнопку запустить в примере