Code Ready | Frontend
21.6K subscribers
1.3K photos
539 videos
17 files
1.02K links
Авторский канал по Frontend разработке.
Ресурсы, гайды, задачи, шпаргалки.
Информация ежедневно пополняется!

Cотрудничество: @energy_c

РКН: https://clck.ru/3NJCKs
Download Telegram
📱 Reflect API — низкоуровневые операции над объектами!

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

📣 Code Ready | #шпора
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-задачу, чтобы не блокировать интерфейс!

Длительная синхронная работа на 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 | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤7🔥6
🤔 Неожиданно интересная статья на Хабре: «Сложнейшая проблема компьютерных наук: центрирование»!

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

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


📣 Code Ready | #статья
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🔥9🤝5
👩‍💻 Псевдоэлемент для стилизации встроенной кнопки выбора файла внутри <input type="file">.

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

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


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

📣 Code Ready | #свойство
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤7🔥4
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-библиотек:
<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.

📣 Code Ready | #практика
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
❤️ iu5git/Web — практический курс по созданию полноценного веб-приложения!

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

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


📣 Code Ready | #репозиторий
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13👍7❤5
👩‍💻 Псевдокласс, который отражает состояние чекбокса или радиокнопки!

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

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

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

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

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


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

📣 Code Ready | #свойство
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
🤔 Типичный верстальщик — база материалов по frontend-разработке!

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

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

📣 Code Ready | #сайт
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 Interview Cheatsheet — 100 вопросов для подготовки к собеседованиям!

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

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


📣 Code Ready | #репозиторий
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍7🤝4
Не используй overflow: hidden, если нужно только обрезать контент!

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 или другое прокручиваемое значение.

📣 Code Ready | #совет
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.

📣 Code Ready | #свойство
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥10🤝4❤2
<template> — храним HTML-разметку без её отображения на странице

Когда структура элемента известна заранее, собирать её вручную через 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.

📣 Code Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
❤14👍12🔥6
📱 Iterator API — управление итерацией и ленивые преобразования!

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

📣 Code Ready | #шпора
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-элемента не всегда связан с размером окна браузера. Компонент может менять свои размеры из-за изменения родительского контейнера, перестроения 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 | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍8🤝5❤3
✍️ Актуальный разбор на Хабре: «Почему баги кроссбраузерности до сих пор никуда не исчезли»!

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

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


📣 Code Ready | #статья
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤6👍5