Code Ready | Frontend


Гео и язык канала: Россия, Русский
Категория: Технологии


Авторский канал по Frontend разработке.
Ресурсы, гайды, задачи, шпаргалки.
Информация ежедневно пополняется!
Cотрудничество: @energy_c
РКН: https://clck.ru/3NJCKs

Зарегистрирован в РКН
Связанные каналы  |  Похожие каналы

Гео и язык канала
Россия, Русский
Категория
Технологии
Статистика
Фильтр публикаций


✍️ Актуальный разбор на Хабре: «Почему баги кроссбраузерности до сих пор никуда не исчезли»!

В этой статье:
• Разберётесь, почему один и тот же код может вести себя по-разному в Chromium, Safari и Firefox;
• Узнаете, как на отображение сайта влияют ОС, WebView, масштабирование, шрифты и особенности браузерных движков;
• Посмотрите на примеры багов и способы снизить вероятность проблем ещё до релиза.

🔊 Продолжай читать на Habr!


📣 Code Ready | #статья


ResizeObserver — отслеживание размеров элементов на уровне разметки!

Размер 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 позволяет синхронизировать логику приложения с реальными размерами элементов и является более точным инструментом для компонентной архитектуры, чем отслеживание только изменения окна.

📣 Code Ready | #практика


📱 Iterator API — управление итерацией и ленивые преобразования!

Шпаргалка по основным возможностям Iterator API: ручное управление итерацией, досрочное завершение и передача ошибок, создание iterable-объектов и ленивые преобразования через Iterator Helpers. Полезная база для углубления знаний об итераторах, генераторах и обработке последовательностей данных.

📣 Code Ready | #шпора


— храним HTML-разметку без её отображения на странице

Когда структура элемента известна заранее, собирать её вручную через createElement() не всегда удобно: разметка смешивается с логикой и увеличивает объём js. Для таких случаев в HTML существует — элемент для хранения готовой 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:




Открыть




Браузер парсит содержимое как HTML, но не отображает его и не включает в активное дерево документа:

Карточка пользователя


Содержимое шаблона доступно через свойство content, которое представляет собой DocumentFragment:
const template = document.querySelector(
'#card-template'
);

console.log(template.content);
// DocumentFragment

Поэтому искать элементы шаблона необходимо внутри content, а не через сам :
template.querySelector('.card');
// null

template.content.querySelector('.card');
// ...

Для создания независимого экземпляра содержимое шаблона можно импортировать в текущий документ с глубоким копированием:
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')
);

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




У содержимое хранится отдельно и становится частью активного DOM только после создания и вставки экземпляра:




Есть важный нюанс с событиями: обработчики, зарегистрированные через 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);

На практике подходит для карточек, строк таблицы, элементов списка, уведомлений и других повторяемых частей интерфейса с заранее известной структурой:
function createUser(user) {
const fragment = document.importNode(
template.content,
true
);

fragment.querySelector('.card__title').textContent =
user.name;

return fragment;
}

🔥 позволяет хранить HTML-заготовку вне активного DOM и создавать из неё независимые экземпляры по мере необходимости, оставляя структуру в HTML, а работу с данными и поведением — в js.

📣 Code Ready | #практика


👩‍💻 Создаём плавные анимации при переходе между страницами!

Правило @view-transition позволяет управлять поведением View Transition API при навигации между документами. С его помощью можно включить плавные переходы между страницами без необходимости вручную запускать их через JS.

Основные параметры:
• navigation: auto — включает переходы при навигации между страницами;

• types — задаёт типы переходов, которые затем можно использовать для разных анимаций.


@view-transition полезно для многостраничных сайтов: смену страниц можно сделать визуально плавной, а сами анимации настроить через CSS-псевдоэлементы View Transition.

📣 Code Ready | #свойство


Не используй overflow: hidden, если нужно только обрезать контент!

overflow: hidden часто ставят просто для обрезки того, что выходит за границы блока.
.wrapper {
overflow: hidden;
}

Но у него есть побочный эффект: элемент становится контейнером прокрутки, даже если полос прокрутки нет. Это может влиять на поведение position: sticky, программную прокрутку и поиск ближайшего скролл-контейнера.

Если прокрутка вообще не нужна, есть более точное значение.
.wrapper {
overflow: clip;
}

Контент по-прежнему обрезается, но контейнер прокрутки не создаётся. Поэтому sticky внутри такого блока может ориентироваться на внешний скролл-контейнер, а не останавливаться на .wrapper.

Разница особенно заметна в такой структуре.

...

.wrapper { overflow: clip; }

.sidebar {
position: sticky;
top: 16px;
}

Есть ещё полезная деталь: расстояние, на котором происходит обрезка, можно увеличить отдельно.
.wrapper {
overflow: clip;
overflow-clip-margin: 8px;
}

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

🔥 overflow: clip широко доступен в современных браузерах. Важно только не считать его полной заменой hidden: если элемент действительно должен программно прокручиваться, нужен overflow: hidden или другое прокручиваемое значение.

📣 Code Ready | #совет


😎 CSS Interview Cheatsheet — 100 вопросов для подготовки к собеседованиям!

В репозитории собраны вопросы по CSS и CSS-фреймворкам, которые помогают проверить не только знание синтаксиса, но и понимание более сложных нюансов верстки и стилизации. Удобно использовать для самопроверки и поиска тем, которые стоит повторить перед техническим интервью.

Оставляю ссылочку: GitHub 📱


📣 Code Ready | #репозиторий


🤔 Типичный верстальщик — база материалов по frontend-разработке!

Сайт с большой подборкой статей, руководств и справочных материалов для веб-разработчиков. Основное внимание уделено HTML, CSS, JavaScript и современной вёрстке — от базовых принципов до практических приёмов, которые используются в проектах. На сайте можно найти материалы по Flexbox и Grid, адаптивной вёрстке, типографике, доступности, оптимизации сайтов, работе с изображениями, CSS-анимациям и др.

📌 Оставляю ссылочку: tpverstak.ru

📣 Code Ready | #сайт


👩‍💻 Псевдокласс, который отражает состояние чекбокса или радиокнопки!

Псевдокласс :checked в CSS применяется к отмеченным элементам формы — например, checkbox и radio. Он позволяет менять их оформление в зависимости от текущего состояния.

Примеры использования:
• менять цвет выбранного элемента;

• создавать кастомные чекбоксы и радиокнопки;

• стилизовать связанные элементы через CSS-селекторы;

• создавать простые переключатели без JavaScript.


По задумке должен работать и с , но поскольку выпадающий список сильно отличается от системы к системе и от браузера к браузеру, то пока работает только в браузере Chrome на Windows.

📣 Code Ready | #свойство


❤️ iu5git/Web — практический курс по созданию полноценного веб-приложения!

В репозитории собраны материалы по всему циклу разработки: от проектирования архитектуры и базы данных до создания REST API, frontend-приложения на React, авторизации, Docker-развертывания и подготовки технической документации. Отличный материал для изучения разработки.

Оставляю ссылочку: GitHub 📱


📣 Code Ready | #репозиторий


Stacking context — управление слоями интерфейса через CSS-контексты!

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-библиотек:





Также необходимо учитывать, что не все проблемы перекрытия связаны со 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.

📣 Code Ready | #практика


👩‍💻 Псевдоэлемент для стилизации встроенной кнопки выбора файла внутри .

::file-selector-button — псевдоэлемент, представляющий кнопку внутри поля . Позволяет стилизовать её так же, как любую кнопку интерфейса: менять фон, бордеры, скругления, состояния наведения/фокуса и другие стили.

С его помощью можно настроить:
• background — цвет фона;
• color — цвет текста;
• border — рамку кнопки;
• padding — внутренние отступы;
• font — оформление текста.


Для лучшей доступности обеспечьте заметные стили фокуса (outline, outline-offset).

📣 Code Ready | #свойство


🤔 Неожиданно интересная статья на Хабре: «Сложнейшая проблема компьютерных наук: центрирование»!

В этой статье:
• Разберётесь, почему Flexbox и Grid не гарантируют визуально идеальное выравнивание;
• Узнаете, как метрики шрифтов и высота строки влияют на положение текста;
• Посмотрите, почему иконки рядом с текстом часто «съезжают» и как это исправлять на практике.

🔊 Продолжай читать на Habr!


📣 Code Ready | #статья


scheduler.yield() — разбиваем долгую JS-задачу, чтобы не блокировать интерфейс!

Длительная синхронная работа на 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, а только разбивает их выполнение во времени.

📣 Code Ready | #практика


📱 Reflect API — низкоуровневые операции над объектами!

Разбираем Reflect API и его основные методы для чтения, изменения, проверки и удаления свойств, вызова функций, создания экземпляров, работы с дескрипторами и получения собственных ключей объекта. Reflect применяется для метапрограммирования, низкоуровневой работы с объектами и реализации Proxy-логики с явным результатом операций.

📣 Code Ready | #шпора


👩‍💻 Можно ли изменить стиль поля, которое браузер заполнил автоматически?

:autofill — псевдокласс, который находит поля формы, куда браузер сам подставил сохранённые данные: адрес, телефон, почту. Нужен, чтобы подсвеченные браузером поля не выбивались из дизайна формы.

Как работает автозаполнение:
• срабатывает только по инициативе браузера, через JavaScript вызвать его нельзя;
• браузер может заполнить сразу несколько полей формы;
• поле остаётся :autofill, пока пользователь не изменит значение;
• после ручного редактирования оно больше не считается автозаполненным.


Не полагайтесь на :autofill для валидации: это лишь визуальное состояние, а не факт корректности данных.

📣 Code Ready | #свойство


👩‍💻 Тег для перевода данных с человеческого языка на компьютерный!

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

В используются:
• содержимое тега — текст, который видит пользователь;
• value — машиночитаемое значение;
• JavaScript — при необходимости может получить значение через свойство value.


Элемент стоит использовать, когда вы не хотите выделять элементы на вёрстке, например, при помощи или , но вам нужно связать текст с внутренним ID или значением, который вы будете использовать при обработке запросов пользователя.

📣 Code Ready | #атрибут


inert — отключение целой области интерфейса без обхода дочерних элементов!

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

Управлять состоянием каждого элемента отдельно можно, но такой подход требует учитывать разные типы элементов и механизмы взаимодействия:
button.disabled = true;
input.disabled = true;
link.tabIndex = -1;

disabled применим не ко всем HTML-элементам, а изменение tabindex решает только задачу фокуса. Для блокировки целой области документа существует глобальный атрибут inert:

Сохранить
Профиль



Состоянием можно управлять через HTMLElement.inert, без поиска и изменения каждого интерактивного элемента внутри контейнера:
const content = document.querySelector('#content');

content.inert = true;

После установки inert контейнер и его потомки в плоском дереве (flat tree) не могут получать фокус. Это относится и к программному вызову focus():
content.inert = true;

document.querySelector('#content input').focus();

Пользовательская активация также блокируется: клик пользователя по кнопке внутри inert-области не приводит к click на этой кнопке. Программный element.click() при этом следует рассматривать отдельно:
const button = document.querySelector('#content button');

button.addEventListener('click', () => {
console.log('click');
});

content.inert = true;

В отличие от inert, pointer-events: none влияет на определение элемента под указателем, но сам по себе не исключает элементы из последовательной навигации с клавиатуры:
.content {
pointer-events: none;
}

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

Например, основной интерфейс можно деактивировать при открытии выдвижной панели, расположенной вне inert-контейнера:








Состояние всего фонового интерфейса переключается одной операцией независимо от количества элементов внутри main:
function openDrawer() {
content.inert = true;
drawer.hidden = false;
}

function closeDrawer() {
content.inert = false;
drawer.hidden = true;
}

Для модальных интерфейсов есть . Вызов showModal() помещает диалог в верхний слой (top layer), а остальную часть документа браузер делает inert автоматически:
const dialog = document.querySelector('dialog');

dialog.showModal();

Сам inert не задаёт визуальное состояние заблокированной области. При необходимости оно оформляется отдельно через CSS:
[inert] {
opacity: 0.6;
}

🔥 inert позволяет исключить целую область документа из взаимодействия без ручного управления disabled, tabindex и состоянием каждого вложенного элемента.

📣 Code Ready | #практика


13 12


🔴AI кодинг интервью с разработчиком из международного FinTech в четверг в 19:00

ДА! Вайбкодинг реально начали проверять на интервью, поэтому мы нашли собеседующего, который проводит AI-секцию в международном финтехе, чтобы вы увидели что на ней спрашивают и как к ней подготовиться

Как это будет:
📂 Александр Дмитриев, разработчик из известного международного финтеха, ex-VK, ex-Ozon проведет вайбкодинг секцию разработчику-добровольцу
📂 Александр будет задавать реальные вопросы с секций, которые проводил сам и комментировать ответы
📂 В конце можно будет задать любой вопрос Александру

Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для разработчиков, которые хотят сменить работу, повысить свой грейд, ЗП и прокачать скиллы.

Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_front_bot

Реклама.
О рекламодателе.

Показано 20 последних публикаций.