Понимание offsetWidth, clientWidth, scrollWidth и -Hight, соответственно

В StackOverflow есть несколько вопросов относительно offsetWidth/clientWidth/scrollWidth (и -Height, соответственно), но никто не дает исчерпывающего объяснения того, что эти значения.

Кроме того, в Интернете есть несколько источников, содержащих путаную или неверную информацию.

Можете ли вы дать полное объяснение, включая некоторые визуальные подсказки? Кроме того, как эти значения могут использоваться для вычисления ширины полосы прокрутки?

Ответ 1

Кодовая модель CSS довольно сложна, особенно когда речь заходит о прокручивании содержимого. Хотя браузер использует значения из вашего CSS для рисования ящиков, определение всех измерений с помощью JS не является прямым, если у вас есть только CSS.

Чтобы каждый элемент имел шесть свойств DOM для вашего удобства: offsetWidth, offsetHeight, clientWidth, clientHeight, scrollWidth и scrollHeight. Это атрибуты только для чтения, представляющие текущий визуальный макет, и все они являются целыми числами (таким образом, возможно, они подвержены ошибкам округления).

Прочтите их подробно:

  • offsetWidth, offsetHeight: размер окна видимости всех границ. Может быть вычислен путем добавления width/height и paddings и границ, если элемент имеет display: block
  • clientWidth, clientHeight: визуальная часть содержимого коробки, не включая границы или полосы прокрутки, но включает в себя дополнение. Невозможно вычислить непосредственно из CSS, зависит от размера полосы прокрутки системы.
  • scrollWidth, scrollHeight: размер всего содержимого коробки, включая части, которые в настоящее время скрыты за пределами области прокрутки. Не может быть рассчитан непосредственно из CSS, зависит от содержимого.

CSS2 Box Model

Попробуйте: jsFiddle


Так как offsetWidth учитывает ширину полосы прокрутки, мы можем использовать ее для вычисления ширины полосы прокрутки по формуле

scrollbarWidth = offsetWidth - clientWidth - getComputedStyle().borderLeftWidth - getComputedStyle().borderRightWidth

К сожалению, мы можем получить ошибки округления, так как offsetWidth и clientWidth всегда являются целыми числами, а фактические размеры могут быть дробными с уровнями масштабирования, отличными от 1.

Обратите внимание, что это

scrollbarWidth = getComputedStyle().width + getComputedStyle().paddingLeft + getComputedStyle().paddingRight - clientWidth

работает не в Chrome, поскольку Chrome возвращает width с уже вычисленной полосой прокрутки. (Кроме того, Chrome отображает paddingBottom в нижней части содержимого прокрутки, в то время как другие браузеры этого не делают)

Ответ 2

Если вы хотите использовать scrollWidth, чтобы получить "REAL" СОДЕРЖАНИЕ /HEIGHT (поскольку контент может быть БОЛЬШЕ, чем css-defined width/height-Box) scrollWidth/Height очень НЕПРАВИЛЬНО, так как некоторые браузеры кажутся "ПЕРЕМЕЩАЮТ" paddingRIGHT и paddingBOTTOM, если контент большой. Затем они помещают прокладки в ПРАВО/НИЖНУЮ часть "слишком широкого/высокого содержания" (см. Рисунок ниже).

== > Поэтому, чтобы получить REAL CONTENT WIDTH в некоторых браузерах, вы должны вычесть BOTH paddings из прокрутки, а в некоторых браузерах вам нужно только вычесть LEFT Padding.

Я нашел решение для этого и хотел бы добавить это как комментарий, но не был разрешен. Поэтому я сделал снимок и сделал его немного более четким в отношении "перемещенных проводов" и "ненадежной прокруткиWidth". В BLUE AREA вы найдете мое решение о том, как получить "РЕАЛЬНЫЙ" СОДЕРЖАНИЕ СОДЕРЖАНИЯ!

Надеюсь, это поможет сделать вещи еще более ясными!

введите описание изображения здесь

Ответ 3

Существует хорошая статья о MDN, которая объясняет теорию этих концепций: https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model/Determining_the_dimensions_of_elements

Он также объясняет важные концептуальные различия между boundingClientRect width/height vs offsetWidth/offsetHeight.

Затем, чтобы доказать правильность или неправильность теории, вам нужны некоторые тесты. Это то, что я сделал здесь: https://github.com/lingtalfi/dimensions-cheatsheet

Это тестирование для chrome53, ff49, safari9, edge13 и ie11.

Результаты испытаний доказывают, что теория в целом правильная. Для тестов я создал 3 divs, содержащих 10 лорам ipsum абзацев каждый. Для них был применен некоторый css:

.div1{
    width: 500px;
    height: 300px;
    padding: 10px;
    border: 5px solid black;
    overflow: auto;
}
.div2{
    width: 500px;
    height: 300px;
    padding: 10px;
    border: 5px solid black;
    box-sizing: border-box;
    overflow: auto;
}

.div3{
    width: 500px;
    height: 300px;
    padding: 10px;
    border: 5px solid black;
    overflow: auto;
    transform: scale(0.5);
}

И вот результаты:

  • div1

    • offsetWidth: 530 (chrome53, ff49, safari9, edge13, ie11)
    • offsetHeight: 330 (chrome53, ff49, safari9, edge13, ie11)
    • bcr.width: 530 (chrome53, ff49, safari9, edge13, ie11)
    • bcr.height: 330 (chrome53, ff49, safari9, edge13, ie11)

    • clientWidth: 505 (chrome53, ff49, safari9)

    • clientWidth: 508 (edge13)
    • clientWidth: 503 (ie11)
    • clientHeight: 320 (chrome53, ff49, safari9, edge13, ie11)

    • scrollWidth: 505 (chrome53, safari9, ff49)

    • scrollWidth: 508 (edge13)
    • scrollWidth: 503 (ie11)
    • scrollHeight: 916 (chrome53, safari9)
    • scrollHeight: 954 (ff49)
    • scrollHeight: 922 (edge13, ie11)
  • div2

    • offsetWidth: 500 (chrome53, ff49, safari9, edge13, ie11)
    • offsetHeight: 300 (chrome53, ff49, safari9, edge13, ie11)
    • bcr.width: 500 (chrome53, ff49, safari9, edge13, ie11)
    • bcr.height: 300 (chrome53, ff49, safari9)
    • bcr.height: 299.9999694824219 (edge13, ie11)
    • clientWidth: 475 (chrome53, ff49, safari9)
    • clientWidth: 478 (edge13)
    • clientWidth: 473 (ie11)
    • clientHeight: 290 (chrome53, ff49, safari9, edge13, ie11)

    • scrollWidth: 475 (chrome53, safari9, ff49)

    • scrollWidth: 478 (edge13)
    • scrollWidth: 473 (ie11)
    • scrollHeight: 916 (chrome53, safari9)
    • scrollHeight: 954 (ff49)
    • scrollHeight: 922 (edge13, ie11)
  • div3

    • offsetWidth: 530 (chrome53, ff49, safari9, edge13, ie11)
    • offsetHeight: 330 (chrome53, ff49, safari9, edge13, ie11)
    • bcr.width: 265 (chrome53, ff49, safari9, edge13, ie11)
    • bcr.height: 165 (chrome53, ff49, safari9, edge13, ie11)
    • clientWidth: 505 (chrome53, ff49, safari9)
    • clientWidth: 508 (edge13)
    • clientWidth: 503 (ie11)
    • clientHeight: 320 (chrome53, ff49, safari9, edge13, ie11)

    • scrollWidth: 505 (chrome53, safari9, ff49)

    • scrollWidth: 508 (edge13)
    • scrollWidth: 503 (ie11)
    • scrollHeight: 916 (chrome53, safari9)
    • scrollHeight: 954 (ff49)
    • scrollHeight: 922 (edge13, ie11)

Таким образом, кроме значения высоты boundingClientRect (299.9999694824219 вместо ожидаемого 300) в edge13 и ie11, результаты подтверждают, что теория, стоящая за этим, работает.

Оттуда, вот мое определение этих понятий:

  • offsetWidth/offsetHeight: размеры рамки макета
  • boundingClientRect: размеры окна рамки рендеринга
  • clientWidth/clientHeight: размеры видимой части поля заполнения макета (исключая полосы прокрутки)
  • scrollWidth/scrollHeight: размеры поля заполнения макета, если он не был ограничен полосами прокрутки.

Примечание. По умолчанию ширина вертикальной полосы прокрутки по умолчанию составляет 12 пикселей в edge13, 15px в chrome53, ff49 и safari9 и 17px в ie11 (выполняется путем измерений в фотошопе со скриншотов и проверяется по результатам тестов).

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

Таким образом, с учетом определений этих понятий ширина вертикальной полосы прокрутки должна быть равна (в псевдокоде):

  • размер макета: offsetWidth - clientWidth - (borderLeftWidth + borderRightWidth)

  • размер рендеринга: boundingClientRect.width - clientWidth - (borderLeftWidth + borderRightWidth)

Обратите внимание, что если вы не понимаете макет и рендеринг, прочитайте статью mdn.

Кроме того, если у вас есть другой браузер (или вы хотите увидеть результаты тестов для себя), вы можете увидеть мою тестовую страницу здесь: http://codepen.io/lingtalfi/pen/BLdBdL

Ответ 4

Я создал более полную и более чистую версию, которую некоторые люди могут найти полезной для запоминания того, какое имя соответствует какому значению. Я использовал цветовой код Chrome Dev Tool, и метки упорядочены симметрично, чтобы быстрее подобрать аналоги:

введите описание изображения здесь

  • Примечание 1: clientLeft также включает в себя ширину вертикальной прокрутки bar, если направление текста установлено справа налево (поскольку бар отображается в этом случае слева)

  • Примечание 2: самая внешняя строка представляет ближайший расположенный родительский (элемент, у которого свойство position установлено значение, отличное от static или initial). Таким образом, если прямой контейнер не является позиционированным элемент, то строка не представляет собой первый контейнер в иерархия, но еще один элемент выше в иерархии. Если нет позиционированный, браузер будет использовать html или body элемент как ссылка


Надеюсь, что кто-нибудь посчитает это полезным, только мои 2 цента;)