Js offsetwidth не работает

Размеры и прокрутка элементов

Материал на этой странице устарел, поэтому скрыт из оглавления сайта.

Более новая информация по этой теме находится на странице https://learn.javascript.ru/size-and-scroll.

Для того, чтобы показывать элементы на произвольных местах страницы, необходимо во-первых, знать CSS-позиционирование, а во-вторых – уметь работать с «геометрией элементов» из JavaScript.

В этой главе мы поговорим о размерах элементов DOM, способах их вычисления и метриках – различных свойствах, которые содержат эту информацию.

Образец документа

Мы будем использовать для примера вот такой элемент, у которого есть рамка (border), поля (padding), и прокрутка:

У него нет отступов margin , в этой главе они не важны, так как метрики касаются именно размеров самого элемента, отступы в них не учитываются.

Результат выглядит так:

В иллюстрации выше намеренно продемонстрирован самый сложный и полный случай, когда у элемента есть ещё и полоса прокрутки.

В этом случае полоса прокрутки «отодвигает» содержимое вместе с padding влево, отбирая у него место.

Именно поэтому ширина содержимого обозначена как content width и равна 284px , а не 300px , как в CSS.

Точное значение получено в предположении, что ширина полосы прокрутки равна 16px , то есть после её вычитания на содержимое остаётся 300 — 16 = 284px . Конечно, она сильно зависит от браузера, устройства, ОС.

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

На рисунке выше поля padding изображены пустыми, но текст там вполне может быть, к примеру, при наличии вертикальной прокрутки.

Метрики

У элементов существует ряд свойств, содержащих их внешние и внутренние размеры. Мы будем называть их «метриками».

Метрики, в отличие от свойств CSS, содержат числа, всегда в пикселях и без единиц измерения на конце.

Вот общая картина:

На картинке все они с трудом помещаются, но, как мы увидим далее, их значения просты и понятны.

Будем исследовать их снаружи элемента и вовнутрь.

offsetParent, offsetLeft/Top

Ситуации, когда эти свойства нужны, можно перечислить по пальцам. Они возникают действительно редко. Как правило, эти свойства используют, потому что не знают средств правильной работы с координатами, о которых мы поговорим позже.

Несмотря на то, что эти свойства нужны реже всего, они – самые «внешние», поэтому начнём с них.

В offsetParent находится ссылка на родительский элемент в смысле отображения на странице.

Уточним, что имеется в виду.

Когда браузер рисует страницу, то он высчитывает дерево расположения элементов, иначе говоря «дерево геометрии» или «дерево рендеринга», которое содержит всю информацию о размерах.

При этом одни элементы естественным образом рисуются внутри других. Но, к примеру, если у элемента стоит position:absolute , то его расположение вычисляется уже не относительно непосредственного родителя parentNode , а относительно ближайшего позиционированного элемента (т.е. свойство position которого не равно static ), или BODY , если такой отсутствует.

Получается, что элемент имеет в дополнение к обычному родителю в DOM – ещё одного «родителя по позиционированию», то есть относительно которого он рисуется. Этот элемент и будет в свойстве offsetParent .

Читайте также:  Как отремонтировать резонатор глушителя

Свойства offsetLeft/Top задают смещение относительно offsetParent .

В примере ниже внутренний

offsetWidth/Height

Теперь переходим к самому элементу.

Эти два свойства – самые простые. Они содержат «внешнюю» ширину/высоту элемента, то есть его полный размер, включая рамки border .

Для нашего элемента:

  • offsetWidth = 390 – внешняя ширина блока, её можно получить сложением CSS-ширины ( 300px , но её часть на рисунке выше отнимает прокрутка, поэтому 284 + 16 ), полей( 2*20px ) и рамок ( 2*25px ).
  • offsetHeight = 290 – внешняя высота блока.

Координаты и размеры в JavaScript устанавливаются только для видимых элементов.

Для элементов с display:none или находящихся вне документа дерево рендеринга не строится. Для них метрики равны нулю. Кстати, и offsetParent для таких элементов тоже null .

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

  • Работает, даже если родителю элемента установлено свойство display:none .
  • Работает для всех элементов, кроме TR , с которым возникают некоторые проблемы в разных браузерах. Обычно, проверяются не TR , поэтому всё ок.
  • Считает элемент видимым, даже если позиционирован за пределами экрана или имеет свойство visibility:hidden .
  • «Схлопнутый» элемент, например пустой div без высоты и ширины, будет считаться невидимым.

clientTop/Left

Далее внутри элемента у нас рамки border .

Для них есть свойства-метрики clientTop и clientLeft .

В нашем примере:

  • clientLeft = 25 – ширина левой рамки
  • clientTop = 25 – ширина верхней рамки

…Но на самом деле они – вовсе не рамки, а отступ внутренней части элемента от внешней.

В чём же разница?

Она возникает тогда, когда документ располагается справа налево (операционная система на арабском языке или иврите). Полоса прокрутки в этом случае находится слева, и тогда свойство clientLeft включает в себя ещё и ширину полосы прокрутки.

clientWidth/Height

Эти свойства – размер элемента внутри рамок border .

Они включают в себя ширину содержимого width вместе с полями padding , но без прокрутки.

На рисунке выше посмотрим вначале на clientHeight , её посчитать проще всего. Прокрутки нет, так что это в точности то, что внутри рамок: CSS-высота 200px плюс верхнее и нижнее поля padding (по 20px ), итого 240px .

На рисунке нижний padding заполнен текстом, но это неважно: по правилам он всегда входит в clientHeight .

Теперь clientWidth – ширина содержимого здесь не равна CSS-ширине, её часть «съедает» полоса прокрутки. Поэтому в clientWidth входит не CSS-ширина, а реальная ширина содержимого 284px плюс левое и правое поля padding (по 20px ), итого 324px .

Если padding нет, то clientWidth/Height в точности равны размеру области содержимого, внутри рамок и полосы прокрутки.

Поэтому в тех случаях, когда мы точно знаем, что padding нет, их используют для определения внутренних размеров элемента.

scrollWidth/Height

Эти свойства – аналоги clientWidth/clientHeight , но с учётом прокрутки.

Свойства clientWidth/clientHeight относятся только к видимой области элемента, а scrollWidth/scrollHeight добавляют к ней прокрученную (которую не видно) по горизонтали/вертикали.

На рисунке выше:

  • scrollHeight = 723 – полная внутренняя высота, включая прокрученную область.
  • scrollWidth = 324 – полная внутренняя ширина, в данном случае прокрутки нет, поэтому она равна clientWidth .
Читайте также:  Counter strike source не работает lan режим

Эти свойства можно использовать, чтобы «распахнуть» элемент на всю ширину/высоту, таким кодом:

Нажмите на кнопку, чтобы распахнуть элемент:

element.style.height = element.scrollHeight + „px“

scrollLeft/scrollTop

Свойства scrollLeft/scrollTop – ширина/высота невидимой, прокрученной в данный момент, части элемента слева и сверху.

Следующая иллюстрация показывает значения scrollHeight и scrollTop для блока с вертикальной прокруткой.

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

При клике на следующий элемент будет выполняться код elem.scrollTop += 10 . Поэтому он будет прокручиваться на 10px вниз:

Не стоит брать width/height из CSS

Мы рассмотрели метрики – свойства, которые есть у DOM-элементов. Их обычно используют для получения их различных высот, ширин и прочих расстояний.

Теперь несколько слов о том, как не надо делать.

Как мы знаем, CSS-высоту и ширину можно установить с помощью elem.style и извлечь, используя getComputedStyle , которые в подробностях обсуждаются в главе Стили и классы.

Получение ширины элемента может быть таким:

Не лучше ли получать ширину так, вместо метрик? Вовсе нет!

Во-первых, CSS-свойства width/height зависят от другого свойства – box-sizing , которое определяет, что такое, собственно, эти ширина и высота. Получается, что изменение box-sizing , к примеру, для более удобной вёрстки, сломает такой JavaScript.

Во-вторых, в CSS свойства width/height могут быть равны auto , например, для инлайн-элемента:

Конечно, с точки зрения CSS размер auto – совершенно нормально, но нам-то в JavaScript нужен конкретный размер в пикселях, который мы могли бы использовать для вычислений. Получается, что в данном случае ширина width из CSS вообще бесполезна.

Есть и ещё одна причина.

Полоса прокрутки – причина многих проблем и недопониманий. Как говорится, «дьявол кроется в деталях». Недопустимо, чтобы наш код работал на элементах без прокрутки и начинал «глючить» с ней.

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

Получается, что реальная ширина содержимого меньше CSS-ширины. И это учитывают свойства clientWidth/clientHeight .

…Но при этом некоторые браузеры также учитывают это в результате getComputedStyle(elem).width , то есть возвращают реальную внутреннюю ширину, а некоторые – именно CSS-свойство. Эти кросс-браузерные отличия – ещё один повод не использовать такой подход, а использовать свойства-метрики.

Если ваш браузер показывает полосу прокрутки (например, под Windows почти все браузеры так делают), то вы можете протестировать это сами, нажав на кнопку в ифрейме ниже.

У элемента с текстом в стилях указано width:300px .

На момент написания этой главы при тестировании в Chrome под Windows alert выводил 283px , а в Firefox – 300px . При этом оба браузера показывали прокрутку. Это из-за того, что Firefox возвращал именно CSS-ширину, а Chrome – реальную ширину, за вычетом прокрутки.

Описанные разночтения касаются только чтения свойства getComputedStyle(. ).width из JavaScript, визуальное отображение корректно в обоих случаях.

Итого

У элементов есть следующие метрики:

  • offsetParent – «родитель по дереву рендеринга» – ближайшая ячейка таблицы, body для статического позиционирования или ближайший позиционированный элемент для других типов позиционирования.
  • offsetLeft/offsetTop – позиция в пикселях левого верхнего угла блока, относительно его offsetParent .
  • offsetWidth/offsetHeight – «внешняя» ширина/высота блока, включая рамки.
  • clientLeft/clientTop – отступ области содержимого от левого-верхнего угла элемента. Если операционная система располагает вертикальную прокрутку справа, то равны ширинам левой/верхней рамки, если же слева (ОС на иврите, арабском), то clientLeft включает в себя прокрутку.
  • clientWidth/clientHeight – ширина/высота содержимого вместе с полями padding , но без полосы прокрутки.
  • scrollWidth/scrollHeight – ширина/высота содержимого, включая прокручиваемую область. Включает в себя padding и не включает полосы прокрутки.
  • scrollLeft/scrollTop – ширина/высота прокрученной части документа, считается от верхнего левого угла.
Читайте также:  Ubuntu как настроить общий доступ

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

Источник

Веб-компонент offsetHeight / offsetWidth 0 при подключении

Я пытаюсь преобразовать мою библиотеку в веб-компонент, и у меня возникла проблема с тем, когда сказать, что компонент был размечен.

У веб-компонентов есть обратный вызов connectedCallback , который выглядит так, как будто это то, что мне нужно, но при проверке себя в обратном вызове мои размеры смещения оба равны нулю:

Когда я ставлю в очередь запрос измерений, измерения существуют, поэтому когда-то между подключением веб-компонента и очередью событий, достигающей следующего элемента, веб-компонент раскладывается механизмом браузера, получая правильные значения измерений:

Мне интересно, если есть обратный вызов, чтобы сказать, когда произошло событие макета?

2 ответа

Я создал простой компонент без shadowDOM. Я написал следующий код в конструкторе:

И не получил никакого выхода. Таким образом, ни одно из этих событий не запускается на веб-компоненте.

Затем я добавил этот код вне компонента:

И эта строка для конструктора:

На моем конкретном компоненте мой callback никогда не вызывался.

У меня был другой компонент, который меняет своих потомков, и был вызван обратный вызов.

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

Лично я бы использовал setTimeout , как вы, так как это кажется единственным надежным способом получить вызов, когда компонент сделан. Хотя вам, возможно, придется указать, куда именно вы положили setTimeout , чтобы убедиться, что вы полностью отобразили DOM.

Эти значения зависят от всего DOM, поэтому необходимо перекрасить DOM. которого еще не произошло .

Если бы каждый элемент вызывал перерисовку DOM, мы бы все жаловались на производительность.

Браузер решает (специфическая для браузера логика), когда делать перерисовку DOM.

Обычный и принятый способ выполнения кода после перерисовки DOM — это setTimeout( FUNC , 0 )

Строго говоря, это не принудительно перерисовывает DOM, а браузер решает, когда перерисовывать
Если у вас есть долго работающий скрипт . FUNC выполнит после , долго работающий скрипт будет выполнен.

Да, мероприятие было бы хорошо .
но это занимает циклы процессора . и в 997 из 1000 случаев вам не нужна информация
.. таким образом, нет события

Так что в 0,03% случаев, когда вам нужна информация о DOM, вы делаете setTimeout

Обратите внимание, что это не относится к пользовательскому элементу, к appendChild вызовам применяется то же самое.

Если у вас есть sh * pload компонентов, которые нуждаются в этой информации о смещении, вы могли бы захотеть взглянуть на Framework, который выполняет виртуальный анализ DOM

Источник

Оцените статью