Разбираем Reflect API и его основные методы для чтения, изменения, проверки и удаления свойств, вызова функций, создания экземпляров, работы с дескрипторами и получения собственных ключей объекта. Reflect применяется для метапрограммирования, низкоуровневой работы с объектами и реализации Proxy-логики с явным результатом операций.Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤7🔥7
scheduler.yield() — разбиваем долгую JS-задачу, чтобы не блокировать интерфейс!
Длительная синхронная работа на
Классический способ разбить такую работу — планировать продолжение через
Это принципиально отличается от
Делать
Поддержка
🔥
📣 Code Ready | #практика
Длительная синхронная работа на
main thread блокирует выполнение других задач. Даже обычный цикл может превратиться в длительную задачу, если обработка элементов достаточно тяжёлая:function processItems(items) {
for (const item of items) {
renderItem(item);
}
}Классический способ разбить такую работу — планировать продолжение через
setTimeout(). В современных браузерах для добровольной передачи управления планировщику есть более специализированный механизм — scheduler.yield():async function processItems(items) {
for (let i = 0; i < items.length; i++) {
renderItem(items[i]);
if ((i + 1) % 100 === 0) {
await scheduler.yield();
}
}
}scheduler.yield() приостанавливает текущую async-функцию и планирует её продолжение как отдельную задачу, давая браузеру возможность выполнить другую ожидающую работу на main thread:await scheduler.yield();
// выполнение продолжится позже
Это принципиально отличается от
await Promise.resolve(): resolved Promise продолжает выполнение через microtask queue и не обеспечивает полноценную передачу управления event loop для выполнения следующей задачи:await Promise.resolve();
// продолжение выполняется как microtask
Делать
yield() после каждой итерации не стоит: каждая передача управления создаёт дополнительные накладные расходы. Размер порции работы зависит от стоимости конкретных операций, поэтому 100 или 200 итераций — лишь пример, а не универсальное значение.if ((i + 1) % CHUNK_SIZE === 0) {
await yieldToMain();
}Поддержка
scheduler.yield() пока не универсальна, поэтому стоит предусмотреть запасной вариант:function yieldToMain() {
if (globalThis.scheduler?.yield) {
return scheduler.yield();
}
return new Promise(resolve => setTimeout(resolve, 0));
}scheduler.yield() полезен для кооперативного планирования длительной работы на main thread: обработки больших наборов данных и постепенного обновления сложного UI. Для тяжёлых вычислений, интенсивно использующих процессор, Worker остаётся предпочтительным решением: yield() не переносит вычисления с main thread, а только разбивает их выполнение во времени.Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤7🔥6
В этой статье:
• Разберётесь, почему Flexbox и Grid не гарантируют визуально идеальное выравнивание;• Узнаете, как метрики шрифтов и высота строки влияют на положение текста;• Посмотрите, почему иконки рядом с текстом часто «съезжают» и как это исправлять на практике.🔊 Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🔥9🤝5
::file-selector-button — псевдоэлемент, представляющий кнопку внутри поля <input type="file">. Позволяет стилизовать её так же, как любую кнопку интерфейса: менять фон, бордеры, скругления, состояния наведения/фокуса и другие стили.С его помощью можно настроить:
• background — цвет фона;
• color — цвет текста;
• border — рамку кнопки;
• padding — внутренние отступы;
• font — оформление текста.
Для лучшей доступности обеспечьте заметные стили фокуса (
outline, outline-offset).Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤7🔥4
Stacking context — управление слоями интерфейса через CSS-контексты!
Например,
Если
Stacking context создаётся не только через
В реальных проектах чаще всего проблемы возникают у модальных окон, dropdown, popover и tooltip-компонентов, когда один из родителей создаёт собственный stacking context:
В такой ситуации увеличение
Отдельный случай —
Теперь
Для глобальных интерфейсных слоёв используют отдельный DOM-слот вне основного дерева приложения. Такой подход лежит в основе portal-механизма современных UI-библиотек:
Также необходимо учитывать, что не все проблемы перекрытия связаны со stacking context. Например,
При отладке проблем со слоями необходимо проверять всю цепочку родителей: наличие stacking context, ограничения
🔥
📣 Code Ready | #практика
z-index не является глобальной системой нумерации слоёв страницы. Браузер строит дерево stacking context, где каждый контекст отрисовывается независимо. Значение z-index сравнивается только внутри текущего контекста и не позволяет дочернему элементу напрямую влиять на соседние контексты.Например,
z-index: 999999 у вложенного элемента не гарантирует, что он окажется выше элемента с меньшим значением z-index, если родительский stacking context находится ниже в порядке отрисовки:.card {
position: relative;
z-index: 1;
}
.modal {
position: absolute;
z-index: 999999;
}
.header {
position: relative;
z-index: 10;
}Если
.modal находится внутри .card, браузер сначала сравнивает .card и .header в родительском stacking context, а уже затем учитывает внутренние слои .card. Высокий z-index дочернего элемента не может изменить положение родительского контекста.Stacking context создаётся не только через
position и z-index. Многие CSS-свойства меняют модель композиции слоёв и создают отдельный контекст наложения:.component {
opacity: 0.99;
transform: translateZ(0);
filter: blur(5px);
isolation: isolate;
contain: paint;
}В реальных проектах чаще всего проблемы возникают у модальных окон, dropdown, popover и tooltip-компонентов, когда один из родителей создаёт собственный stacking context:
.layout {
transform: translate3d(0, 0, 0);
}
.dropdown {
position: absolute;
z-index: 999999;
}В такой ситуации увеличение
z-index не помогает, потому что элемент остаётся внутри ограниченного контекста .layout.Отдельный случай —
position: fixed. Обычно такой элемент привязан к viewport, но некоторые свойства родителей изменяют его containing block:.page {
transform: translateZ(0);
}
.modal {
position: fixed;
inset: 0;
z-index: 10000;
}Теперь
modal рассчитывает позицию относительно .page, а не viewport. Поэтому проблема решается не изменением z-index, а изменением структуры DOM.Для глобальных интерфейсных слоёв используют отдельный DOM-слот вне основного дерева приложения. Такой подход лежит в основе portal-механизма современных UI-библиотек:
<body>
<div id="app"></div>
<div id="overlay-root"></div>
</body>
Также необходимо учитывать, что не все проблемы перекрытия связаны со stacking context. Например,
overflow: hidden может обрезать дочерний элемент независимо от его z-index:.container {
overflow: hidden;
}
.dropdown {
position: absolute;
z-index: 1000;
}При отладке проблем со слоями необходимо проверять всю цепочку родителей: наличие stacking context, ограничения
overflow, изменённый containing block и фактическое расположение элементов в DOM.z-index — это только один параметр системы композиции браузера. В сложных интерфейсах реальный порядок отображения определяется структурой stacking context.Please open Telegram to view this post
VIEW IN TELEGRAM
👍10🔥5🤝4❤1
This media is not supported in your browser
VIEW IN TELEGRAM
В репозитории собраны материалы по всему циклу разработки: от проектирования архитектуры и базы данных до создания REST API, frontend-приложения на React, авторизации, Docker-развертывания и подготовки технической документации. Отличный материал для изучения разработки.
Оставляю ссылочку: GitHub📱
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13👍7❤5
Псевдокласс
:checked в CSS применяется к отмеченным элементам формы — например, checkbox и radio. Он позволяет менять их оформление в зависимости от текущего состояния.Примеры использования:
• менять цвет выбранного элемента;
• создавать кастомные чекбоксы и радиокнопки;
• стилизовать связанные элементы через CSS-селекторы;
• создавать простые переключатели без JavaScript.
По задумке должен работать и с
<option>, но поскольку выпадающий список сильно отличается от системы к системе и от браузера к браузеру, то пока работает только в браузере Chrome на Windows.Please open Telegram to view this post
VIEW IN TELEGRAM
❤11👍11🤝3🔥2
This media is not supported in your browser
VIEW IN TELEGRAM
Сайт с большой подборкой статей, руководств и справочных материалов для веб-разработчиков. Основное внимание уделено HTML, CSS, JavaScript и современной вёрстке — от базовых принципов до практических приёмов, которые используются в проектах. На сайте можно найти материалы по Flexbox и Grid, адаптивной вёрстке, типографике, доступности, оптимизации сайтов, работе с изображениями, CSS-анимациям и др.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14❤11🤝3👍2😁1
This media is not supported in your browser
VIEW IN TELEGRAM
В репозитории собраны вопросы по CSS и CSS-фреймворкам, которые помогают проверить не только знание синтаксиса, но и понимание более сложных нюансов верстки и стилизации. Удобно использовать для самопроверки и поиска тем, которые стоит повторить перед техническим интервью.
Оставляю ссылочку: GitHub📱
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍7🤝4
Не используй overflow: hidden, если нужно только обрезать контент!
Но у него есть побочный эффект: элемент становится контейнером прокрутки, даже если полос прокрутки нет. Это может влиять на поведение
Если прокрутка вообще не нужна, есть более точное значение.
Контент по-прежнему обрезается, но контейнер прокрутки не создаётся. Поэтому
Разница особенно заметна в такой структуре.
Есть ещё полезная деталь: расстояние, на котором происходит обрезка, можно увеличить отдельно.
Это удобно, когда содержимое нужно обрезать, но хочется сохранить несколько пикселей для визуального эффекта вроде тени.
🔥
📣 Code Ready | #совет
overflow: hidden часто ставят просто для обрезки того, что выходит за границы блока..wrapper {
overflow: hidden;
}Но у него есть побочный эффект: элемент становится контейнером прокрутки, даже если полос прокрутки нет. Это может влиять на поведение
position: sticky, программную прокрутку и поиск ближайшего скролл-контейнера.Если прокрутка вообще не нужна, есть более точное значение.
.wrapper {
overflow: clip;
}Контент по-прежнему обрезается, но контейнер прокрутки не создаётся. Поэтому
sticky внутри такого блока может ориентироваться на внешний скролл-контейнер, а не останавливаться на .wrapper.Разница особенно заметна в такой структуре.
<div class="wrapper">
<aside class="sidebar">...</aside>
</div>
.wrapper { overflow: clip; }
.sidebar {
position: sticky;
top: 16px;
}Есть ещё полезная деталь: расстояние, на котором происходит обрезка, можно увеличить отдельно.
.wrapper {
overflow: clip;
overflow-clip-margin: 8px;
}Это удобно, когда содержимое нужно обрезать, но хочется сохранить несколько пикселей для визуального эффекта вроде тени.
overflow: clip широко доступен в современных браузерах. Важно только не считать его полной заменой hidden: если элемент действительно должен программно прокручиваться, нужен overflow: hidden или другое прокручиваемое значение.Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥9❤5
This media is not supported in your browser
VIEW IN TELEGRAM
Правило
@view-transition позволяет управлять поведением View Transition API при навигации между документами. С его помощью можно включить плавные переходы между страницами без необходимости вручную запускать их через JS.Основные параметры:
• navigation: auto — включает переходы при навигации между страницами;
• types — задаёт типы переходов, которые затем можно использовать для разных анимаций.
@view-transition полезно для многостраничных сайтов: смену страниц можно сделать визуально плавной, а сами анимации настроить через CSS-псевдоэлементы View Transition.Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥10🤝4❤2
<template> — храним HTML-разметку без её отображения на странице
Когда структура элемента известна заранее, собирать её вручную через
Ту же структуру можно описать декларативно непосредственно в HTML:
Браузер парсит содержимое
Содержимое шаблона доступно через свойство
Поэтому искать элементы шаблона необходимо внутри
Для создания независимого экземпляра содержимое шаблона можно импортировать в текущий документ с глубоким копированием:
Аргумент
Один шаблон можно использовать многократно, каждый раз получая независимую DOM-структуру:
У
Есть важный нюанс с событиями: обработчики, зарегистрированные через
Обработчик необходимо назначить уже созданному экземпляру либо использовать делегирование событий:
На практике
🔥
📣 Code Ready | #практика
Когда структура элемента известна заранее, собирать её вручную через
createElement() не всегда удобно: разметка смешивается с логикой и увеличивает объём js. Для таких случаев в HTML существует <template> — элемент для хранения готовой DOM-заготовки без её отображения на странице:const card = document.createElement('article');
const title = document.createElement('h2');
const button = document.createElement('button');
card.className = 'card';
title.className = 'card__title';
button.className = 'card__button';
card.append(title, button);Ту же структуру можно описать декларативно непосредственно в HTML:
<template id="card-template">
<article class="card">
<h2 class="card__title"></h2>
<button class="card__button">
Открыть
</button>
</article>
</template>
Браузер парсит содержимое
<template> как HTML, но не отображает его и не включает в активное дерево документа:<template>
<h2>Карточка пользователя</h2>
</template>
Содержимое шаблона доступно через свойство
content, которое представляет собой DocumentFragment:const template = document.querySelector(
'#card-template'
);
console.log(template.content);
// DocumentFragment
Поэтому искать элементы шаблона необходимо внутри
content, а не через сам <template>:template.querySelector('.card');
// null
template.content.querySelector('.card');
// <article class="card">...</article>Для создания независимого экземпляра содержимое шаблона можно импортировать в текущий документ с глубоким копированием:
const fragment = document.importNode(
template.content,
true
);
document.body.append(fragment);
Аргумент
true означает глубокое копирование вместе со всеми вложенными узлами. До вставки экземпляр можно заполнить данными, не изменяя исходный шаблон:const fragment = document.importNode(
template.content,
true
);
fragment.querySelector('.card__title').textContent =
'JavaScript';
document.body.append(fragment);
Один шаблон можно использовать многократно, каждый раз получая независимую DOM-структуру:
function createCard(title) {
const fragment = document.importNode(
template.content,
true
);
fragment.querySelector('.card__title').textContent =
title;
return fragment;
}
document.body.append(
createCard('JavaScript'),
createCard('TypeScript'),
createCard('Node.js')
);<template> не следует путать с обычным скрытым контейнером: hidden скрывает элемент визуально, но его содержимое остаётся частью документа:<div hidden>
<video src="video.mp4" autoplay></video>
</div>
У
<template> содержимое хранится отдельно и становится частью активного DOM только после создания и вставки экземпляра:<template id="video-template">
<video src="video.mp4" autoplay></video>
</template>
Есть важный нюанс с событиями: обработчики, зарегистрированные через
addEventListener(), при копировании DOM-структуры не переносятся:const button =
template.content.querySelector('.card__button');
button.addEventListener('click', handler);
const fragment = document.importNode(
template.content,
true
);
Обработчик необходимо назначить уже созданному экземпляру либо использовать делегирование событий:
const button =
fragment.querySelector('.card__button');
button.addEventListener('click', handler);
На практике
<template> подходит для карточек, строк таблицы, элементов списка, уведомлений и других повторяемых частей интерфейса с заранее известной структурой:function createUser(user) {
const fragment = document.importNode(
template.content,
true
);
fragment.querySelector('.card__title').textContent =
user.name;
return fragment;
}<template> позволяет хранить HTML-заготовку вне активного DOM и создавать из неё независимые экземпляры по мере необходимости, оставляя структуру в HTML, а работу с данными и поведением — в js.Please open Telegram to view this post
VIEW IN TELEGRAM
❤14👍12🔥6
Шпаргалка по основным возможностям Iterator API: ручное управление итерацией, досрочное завершение и передача ошибок, создание iterable-объектов и ленивые преобразования через Iterator Helpers. Полезная база для углубления знаний об итераторах, генераторах и обработке последовательностей данных.Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12👍7❤6
ResizeObserver — отслеживание размеров элементов на уровне разметки!
Размер DOM-элемента не всегда связан с размером окна браузера. Компонент может менять свои размеры из-за изменения родительского контейнера, перестроения
Например, открытие sidebar может уменьшить рабочую область компонента с 1100px до 900px, при этом размер окна останется прежним.
Событие
Компонент получает информацию о собственном размере и не зависит от того, где он находится: внутри модального окна, боковой панели или полноэкранного блока.
При работе с
Браузер может обнаружить такой цикл и ограничить дальнейшую доставку уведомлений.
Обычно измерение и изменение интерфейса разделяют, например перенося обновление DOM в следующий кадр отрисовки:
Наблюдатель должен иметь собственный жизненный цикл. Создание новых экземпляров при каждом обновлении компонента приводит к лишним подпискам:
Для современных браузеров доступны более точные способы измерения через
🔥
📣 Code Ready | #практика
Размер DOM-элемента не всегда связан с размером окна браузера. Компонент может менять свои размеры из-за изменения родительского контейнера, перестроения
flex или grid-разметки, открытия боковой панели или изменения структуры интерфейса.window.resize отслеживает только изменение размеров viewport и не позволяет узнать, что изменился конкретный элемент.window.addEventListener(
'resize',
() => {
updateLayout();
}
);
Например, открытие sidebar может уменьшить рабочую область компонента с 1100px до 900px, при этом размер окна останется прежним.
Событие
resize не сработает, хотя компоненту уже нужен перерасчёт. Для отслеживания размеров конкретных DOM-элементов используется ResizeObserver:const observer = new ResizeObserver(([entry]) => {
const width = entry.contentRect.width;
renderChart(width);
});
observer.observe(chart);Компонент получает информацию о собственном размере и не зависит от того, где он находится: внутри модального окна, боковой панели или полноэкранного блока.
При работе с
ResizeObserver важно учитывать возможность цикла изменений. Если внутри обработчика менять размер наблюдаемого элемента, это может вызвать повторное изменение и новый вызов наблюдателя:const observer = new ResizeObserver(([entry]) => {
const width = entry.contentRect.width;
element.style.width = `${width + 10}px`;
});Браузер может обнаружить такой цикл и ограничить дальнейшую доставку уведомлений.
Обычно измерение и изменение интерфейса разделяют, например перенося обновление DOM в следующий кадр отрисовки:
const observer = new ResizeObserver(([entry]) => {
const width = entry.contentRect.width;
requestAnimationFrame(() => {
updateUI(width);
});
});requestAnimationFrame помогает разделить чтение размеров и изменение DOM, но не исправляет ситуацию, когда логика постоянно изменяет размеры наблюдаемого элемента.Наблюдатель должен иметь собственный жизненный цикл. Создание новых экземпляров при каждом обновлении компонента приводит к лишним подпискам:
const observer = new ResizeObserver(callback);
observer.observe(element);
// очистка
observer.disconnect();
Для современных браузеров доступны более точные способы измерения через
contentBoxSize и borderBoxSize:const observer = new ResizeObserver(([entry]) => {
const width =
entry.borderBoxSize?.[0]?.inlineSize ??
entry.borderBoxSize?.inlineSize ??
entry.contentRect.width;
updateLayout(width);
});borderBoxSize учитывает padding и border, поэтому подходит для случаев, когда расчёты должны соответствовать фактическому размеру блока.ResizeObserver не заменяет CSS-механизмы адаптивности. Если изменение касается только отображения, лучше использовать container queries:@container (min-width: 600px) {
.card {
display: grid;
}
}ResizeObserver нужен там, где размер элемента влияет на логику приложения: перерасчёт графиков, размеры canvas, виртуализация списков и другие операции, требующие данных о размере DOM-элемента в js.ResizeObserver позволяет синхронизировать логику приложения с реальными размерами элементов и является более точным инструментом для компонентной архитектуры, чем отслеживание только изменения окна.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍8🤝5❤3
В этой статье:
• Разберётесь, почему один и тот же код может вести себя по-разному в Chromium, Safari и Firefox;• Узнаете, как на отображение сайта влияют ОС, WebView, масштабирование, шрифты и особенности браузерных движков;• Посмотрите на примеры багов и способы снизить вероятность проблем ещё до релиза.🔊 Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤6👍5