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


385

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

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

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

Ответы:


869

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

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

Давайте рассмотрим их подробно:

  • offsetWidth, offsetHeight: Размер визуального блока, включая все границы. Можно рассчитать, добавив width/ heightи отступы и границы, если элемент имеет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 внизу содержимого прокрутки, в то время как другие браузеры этого не делают)


27
Для тех, кто ищет более детальную гранулярность, чем целые числа, используйте element.getBoundingClientRect()(см. Примечание на developer.mozilla.org/en-US/docs/Web/API/Element.clientWidth )
Anson Kao

1
Обратите внимание, что в зависимости от вашего макета scrollWidth и scrollHeight могут быть очень полезны для получения размера ваших псевдоэлементов :: before и :: after.
Дэвид

Кроме того , было бы полезно , чтобы объяснить , как те относятся к naturalWidthиnaturalHeight
YakovL

почему scrollHeightвключает, padding-bottomно scrollWidthне включаетpadding-right
JunGor

clientWidthдля document.documentElement.clientWidthотличается , как кажется, включают в себя padding, bordersиmargin
Drenai

50

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

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

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

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


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


30

Если вы хотите использовать scrollWidth , чтобы получить «РЕАЛ» CONTENT ШИРИНА / ВЫСОТА (как содержание может быть больше , чем CSS определенная ширина / высота-Box) scrollWidth / Высота очень ненадежен , как какой - то браузер , кажется, «МАХ» на paddingRIGHT & paddingBOTTOM, если содержание слишком большое. Затем они помещают прокладки в ПРАВО / ВНИЗ «слишком широкого / высокого содержания» (см. Рисунок ниже).

==> Поэтому, чтобы получить РЕАЛЬНОЕ ШИРОКОЕ СОДЕРЖАНИЕ в некоторых браузерах, вы должны вычесть ОБА отступы из полосы прокрутки, а в некоторых браузерах вам нужно только вычесть ЛЕВЫЙ отступ.

Я нашел решение для этого и хотел добавить это как комментарий, но не позволили. Поэтому я сделал снимок и сделал его немного более понятным в отношении «перемещенных отступов» и «ненадежной ширины прокрутки». В СИНИЙ ОБЛАСТИ вы найдете мое решение о том, как получить «РЕАЛЬНУЮ» ШИРИНУ КОНТЕНТА!

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

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


13

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

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

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

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

Результаты испытаний доказывают, что теория в целом верна. Для тестов я создал 3 div, содержащих по 10 абзацев lorem 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)
    • высота: 330 (chrome53, ff49, safari9, edge13, ie11)

    • clientWidth: 505 (chrome53, ff49, safari9)

    • clientWidth: 508 (edge13)
    • clientWidth: 503 (т.е. 11)
    • высота клиента: 320 (chrome53, ff49, safari9, edge13, ie11)

    • scrollWidth: 505 (chrome53, safari9, ff49)

    • scrollWidth: 508 (edge13)
    • scrollWidth: 503 (т.е. 11)
    • Высота свитка: 916 (chrome53, safari9)
    • Высота свитка: 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)
    • высота: 300 (chrome53, ff49, safari9)
    • bcr.height: 299.9999694824219 (edge13, т.е. 11)
    • clientWidth: 475 (chrome53, ff49, safari9)
    • clientWidth: 478 (ребро13)
    • clientWidth: 473 (т.е. 11)
    • clientHeight: 290 (chrome53, ff49, safari9, edge13, ie11)

    • scrollWidth: 475 (chrome53, safari9, ff49)

    • scrollWidth: 478 (edge13)
    • scrollWidth: 473 (т.е. 11)
    • Высота свитка: 916 (chrome53, safari9)
    • Высота свитка: 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)
    • высота: 165 (chrome53, ff49, safari9, edge13, ie11)
    • clientWidth: 505 (chrome53, ff49, safari9)
    • clientWidth: 508 (edge13)
    • clientWidth: 503 (т.е. 11)
    • высота клиента: 320 (chrome53, ff49, safari9, edge13, ie11)

    • scrollWidth: 505 (chrome53, safari9, ff49)

    • scrollWidth: 508 (edge13)
    • scrollWidth: 503 (т.е. 11)
    • Высота свитка: 916 (chrome53, safari9)
    • Высота свитка: 954 (ff49)
    • scrollHeight: 922 (edge13, ie11)

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

Отсюда мое определение этих понятий:

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

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

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

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

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

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.