Размеры и скроллинг элементов в 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 области будут вычисляться уже исходя из них.

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

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

Блочная модель 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-свойств размеры элемента на странице

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

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

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

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

Свойство scrollWidth аналогично clientWidth, но учитывает содержимое .box-wrapper, которое не помещается в область видимости элемента и требует прокрутки. В этом примере она равна 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).

sscrollWidth и scrollHeight - это свойства объекта элемента в JavaScript, которые возвращают размеры области содержимого элемента, включая внутренние отступы (padding) и содержимое, которое не помещается на экране и требует прокрутки. Эти свойства могут использоваться для определения размеров элемента с учетом скрытого содержимого и для управления прокруткой элемента программно.

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

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

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

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

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

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

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

Кроме размеров элемента, нам также очень важно знать как он расположен на странице. В 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. Они показывают количество пикселей, на которое прокручено содержимое элемента по горизонтали и вертикали.

Cвойства 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-свойства, с помощью которых можно как получить положение прокрутки, так и изменить её

Разное

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

<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 со значением которое будет компенсировать ширину прокрутки.

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