Стили, размеры, координаты

Стили, размеры, координаты #

Геометрия и стили элемента нужны, когда UI реагирует на размер окна, рисует оверлеи или фиксирует панель checkout. JavaScript читает вычисленные размеры через getBoundingClientRect, меняет оформление через classList и inline-стили element.style.


classList #

Классы — основной способ переключать состояние без inline-CSS:

const bar = document.querySelector('.checkout-bar')

bar.classList.add('is-visible')
bar.classList.remove('is-hidden')
bar.classList.toggle('is-compact', window.innerWidth < 768)

bar.classList.replace('theme-light', 'theme-dark')

Несколько классов за раз:

document.querySelector('.playlist-panel').classList.add('is-open', 'has-focus')

Состояние виджета «свернут»:

function setWidgetCollapsed(widgetEl, collapsed) {
  widgetEl.classList.toggle('widget--collapsed', collapsed)
  widgetEl.setAttribute('aria-expanded', String(!collapsed))
}

Inline-стили: element.style #

Inline-стили задают конкретные CSS-свойства в camelCase:

const tooltip = document.querySelector('.chart-tooltip')

tooltip.style.left = '120px'
tooltip.style.top = '48px'
tooltip.style.opacity = '1'
tooltip.style.pointerEvents = 'none'

Чтение заданных inline-значений:

tooltip.style.left // "120px"

Вычисленное значение из таблицы стилей — через getComputedStyle:

const styles = getComputedStyle(tooltip)
styles.backgroundColor
parseFloat(styles.paddingTop)

Для анимаций предпочитайте классы и CSS transitions; inline — для координат, зависящих от указателя или scroll.


getBoundingClientRect #

Возвращает размер и положение элемента относительно viewport:

const playBtn = document.querySelector('.player__play')
const rect = playBtn.getBoundingClientRect()

rect.width // ширина с padding и border
rect.height
rect.top // от верхнего края viewport
rect.left
rect.right
rect.bottom

Позиционировать всплывающую подсказку над кнопкой:

function placeTooltip(anchor, tooltip) {
  const { top, left, width } = anchor.getBoundingClientRect()
  tooltip.style.position = 'fixed'
  tooltip.style.left = `${left + width / 2}px`
  tooltip.style.top = `${top - 8}px`
  tooltip.style.transform = 'translate(-50%, -100%)'
}

Координаты меняются при прокрутке — для scroll-контейнеров пересчитывайте на scroll или используйте position: fixed с актуальным rect.


offsetWidth, clientWidth, scrollWidth #

СвойствоЧто включает
offsetWidth / offsetHeightlayout-размер + padding + border
clientWidth / clientHeightвнутренняя область + padding, без border и scrollbar
scrollWidth / scrollHeightполный размер контента, включая скрытое переполнение
const trackList = document.querySelector('.playlist__tracks')

const hasOverflow = trackList.scrollHeight > trackList.clientHeight
trackList.classList.toggle('playlist__tracks--scrollable', hasOverflow)

Размер окна и matchMedia #

const grid = document.querySelector('.dashboard-grid')

function syncGridMode() {
  const compact = window.matchMedia('(max-width: 960px)').matches
  grid.classList.toggle('dashboard-grid--compact', compact)
}

syncGridMode()
window.addEventListener('resize', syncGridMode)

ResizeObserver точнее отслеживает конкретный элемент, а не всё окно — полезно для виджета с графиком фиксированной высоты.


Практика: sticky checkout-bar #

const checkoutBar = document.querySelector('.checkout-bar')
const sentinel = document.querySelector('.checkout-sentinel')

const observer = new IntersectionObserver(
  ([entry]) => {
    checkoutBar.classList.toggle('checkout-bar--pinned', !entry.isIntersecting)
  },
  { rootMargin: '0px', threshold: 0 }
)

observer.observe(sentinel)

Класс checkout-bar--pinned в CSS фиксирует панель; JS только переключает флаг по видимости sentinel-элемента.


Итог #

  • classList — переключение состояний и тем; избегайте лишнего inline-CSS.
  • element.style — точечные координаты и динамика; вычисленные значения — getComputedStyle.
  • getBoundingClientRect — размер и положение относительно viewport для оверлеев.
  • client* / scroll* / offset* — переполнение и внутренние размеры контейнеров.

См. также #

Часто спрашивают

querySelector или getElementById?
getElementById быстрее и проще для одного id; querySelector гибче за счёт CSS-селекторов. Подробнее