Шпаргалка по основным возможностям 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
overscroll-behavior — управление scroll chaining во вложенных контейнерах!
Модальные окна, боковые панели и чаты часто содержат собственные области прокрутки, независимые от основного документа.
Чтобы содержимое стало прокручиваемым, элемент должен быть scroll container. Обычно для этого ограничивают его размер и задают соответствующее значение
Когда прокрутка достигает границы контейнера, дальнейший пользовательский ввод может продолжить прокрутку в родительском scroll container. Этот механизм называется scroll chaining:
Если интерфейс прокручивается только по вертикальной оси, точнее ограничить действие свойства этой осью:
Такой вариант подходит для чатов, меню, сайдбаров, модальных окон и других компонентов с независимой вертикальной прокруткой:
Свойство поддерживает три основных значения:
Ограничение overscroll behavior также может влиять на браузерные действия на границе прокрутки, включая pull-to-refresh и жестовую навигацию в средах, где они поддерживаются.
Поведение можно контролировать и на уровне корневого scroll container, однако глобальное ограничение следует применять только при наличии конкретного требования интерфейса:
Важно отличать управление scroll chaining от background scroll lock. Если при открытой модалке документ вообще не должен прокручиваться, блокировку фонового скролла необходимо реализовывать отдельно.
Для прокручиваемого содержимого модального окна достаточно следующей конфигурации:
После достижения границы
🔥
📣 Code Ready | #практика
Модальные окна, боковые панели и чаты часто содержат собственные области прокрутки, независимые от основного документа.
<div class="modal">
<div class="modal__content">
<!-- длинный контент -->
</div>
</div>
Чтобы содержимое стало прокручиваемым, элемент должен быть scroll container. Обычно для этого ограничивают его размер и задают соответствующее значение
overflow:.modal__content {
max-height: 70vh;
overflow-y: auto;
}Когда прокрутка достигает границы контейнера, дальнейший пользовательский ввод может продолжить прокрутку в родительском scroll container. Этот механизм называется scroll chaining:
.modal__content {
max-height: 70vh;
overflow-y: auto;
overscroll-behavior: contain;
}overscroll-behavior: contain предотвращает scroll chaining за пределы текущего контейнера, сохраняя обычную прокрутку внутри него и локальные overscroll-эффекты.Если интерфейс прокручивается только по вертикальной оси, точнее ограничить действие свойства этой осью:
.sidebar {
height: 100vh;
overflow-y: auto;
overscroll-behavior-y: contain;
}Такой вариант подходит для чатов, меню, сайдбаров, модальных окон и других компонентов с независимой вертикальной прокруткой:
.chat {
overflow-y: auto;
overscroll-behavior-y: contain;
}Свойство поддерживает три основных значения:
auto сохраняет стандартное поведение, contain останавливает scroll chaining, а none дополнительно подавляет локальные overscroll-эффекты:.element {
overscroll-behavior: auto;
}
.element {
overscroll-behavior: contain;
}
.element {
overscroll-behavior: none;
}Ограничение overscroll behavior также может влиять на браузерные действия на границе прокрутки, включая pull-to-refresh и жестовую навигацию в средах, где они поддерживаются.
Поведение можно контролировать и на уровне корневого scroll container, однако глобальное ограничение следует применять только при наличии конкретного требования интерфейса:
html,
body {
overscroll-behavior-y: none;
}
overscroll-behavior не создаёт scroll container самостоятельно. Оно определяет поведение уже существующей области прокрутки при достижении её границ:.panel {
max-height: 500px;
overflow-y: auto;
overscroll-behavior-y: contain;
}Важно отличать управление scroll chaining от background scroll lock. Если при открытой модалке документ вообще не должен прокручиваться, блокировку фонового скролла необходимо реализовывать отдельно.
Для прокручиваемого содержимого модального окна достаточно следующей конфигурации:
.modal__content {
max-height: min(700px, 80vh);
overflow-y: auto;
overscroll-behavior-y: contain;
}После достижения границы
.modal__content вертикальная прокрутка не передаётся родительскому scroll container.overscroll-behavior декларативно контролирует scroll chaining без перехвата wheel и touchmove через js. Для независимых вертикальных scroll containers в большинстве случаев достаточно overscroll-behavior-y: contain.Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤4🔥4🤝3
Большинство собирает такую базу годами.
Здесь все отсортировали и разбили в одном канале
(первое сообщение в закрепе)
Please open Telegram to view this post
VIEW IN TELEGRAM
beforeinput — перехватываем ввод до изменения значения поля
Событие
Вместо исправления значения можно проверить предстоящее действие:
Свойство
Теперь запретим ввод пробелов:
При обычном наборе пробел не попадёт в поле. Но вставку из буфера обмена этот код не блокирует. Кроме того, некоторые операции, например при вводе через IME, нельзя отменить. Поэтому проверка итогового значения всё равно нужна.
С ограничением длины есть ещё один нюанс — проверка
Через
Теперь замена выделенного фрагмента разрешена, если итоговая длина не превышает лимит. Пример рассчитан на обычный ввод в
При этом
В
Метод
Также стоит помнить, что присваивание
🔥
📣 Code Ready | #практика
Событие
input срабатывает уже после изменения значения поля. Если нужно запретить определённые символы, приходится удалять их из готового текста. При этом может сбиваться позиция курсора, особенно при редактировании середины строки. beforeinput позволяет перехватить операцию заранее и отменить её до изменения значения. Например, через input пробелы обычно удаляют так:const input = document.querySelector('#username');
input.addEventListener('input', () => {
input.value = input.value.replace(/\s/g, '');
});Вместо исправления значения можно проверить предстоящее действие:
input.addEventListener('beforeinput', (event) => {
console.log(event.inputType);
console.log(event.data);
});Свойство
inputType показывает тип операции: insertText, insertFromPaste, deleteContentBackward и другие. А data содержит добавляемый текст, если он доступен.Теперь запретим ввод пробелов:
input.addEventListener('beforeinput', (event) => {
if (!event.cancelable) return;
if (
event.inputType === 'insertText' &&
event.data?.includes(' ')
) {
event.preventDefault();
}
});При обычном наборе пробел не попадёт в поле. Но вставку из буфера обмена этот код не блокирует. Кроме того, некоторые операции, например при вводе через IME, нельзя отменить. Поэтому проверка итогового значения всё равно нужна.
С ограничением длины есть ещё один нюанс — проверка
value.length >= 10 заблокирует ввод даже при замене выделенного текста, если поле уже заполнено.Через
beforeinput можно рассчитать длину с учётом выделения:input.addEventListener('beforeinput', (event) => {
if (!event.cancelable || event.inputType !== 'insertText') {
return;
}
const value = input.value;
const start = input.selectionStart ?? 0;
const end = input.selectionEnd ?? start;
const nextLength =
value.length -
(end - start) +
(event.data?.length ?? 0);
if (nextLength > 10) {
event.preventDefault();
}
});Теперь замена выделенного фрагмента разрешена, если итоговая длина не превышает лимит. Пример рассчитан на обычный ввод в
type="text". Для стандартного ограничения пользовательского ввода, включая вставку, проще использовать maxlength="10".При этом
String.length и maxlength считают единицы UTF-16, поэтому эмодзи может занимать больше одной единицы. Программное изменение значения maxlength не ограничивает.В
contenteditable событие позволяет работать и с диапазонами DOM:const editor = document.querySelector('#editor');
editor.addEventListener('beforeinput', (event) => {
console.log(event.inputType);
console.log(event.getTargetRanges());
});Метод
getTargetRanges() возвращает массив StaticRange с диапазонами, которые затронет предполагаемое редактирование. Для обычных input и textarea он возвращает пустой массив — там выделение получают через selectionStart и selectionEnd в поддерживаемых типах полей.Также стоит помнить, что присваивание
input.value = 'new value' не вызывает автоматически ни beforeinput, ни input. Поэтому проверка данных на сервере остаётся обязательной.beforeinput позволяет контролировать конкретную операцию редактирования до изменения текста, а не исправлять результат постфактум. Удобно для масок ввода, сложных ограничений и текстовых редакторов, где важно сохранить курсор и выделение.Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤6🤝5🔥2
Шпаргалка по основным возможностям IndexedDB API: создание и удаление баз данных, версионирование, управление хранилищами, работа с транзакциями и CRUD-операции. Примеры используют db, полученный после успешного открытия базы, и демонстрируют отдельные операции.Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤7🤝3👍1
Утро 8:00. Одни стоят на остановке. Ждут доставку к “любимой” работе.
А Алёна садится за ноутбук в своей уютной квартире на 3 часа, собирает бизнесу чат-бота в Тг, Вк или Максе из готовых блоков и получает 9-10 тыс на карту.
Разница не в таланте. Один знает, что чат-бота можно собрать на конструкторе как лего, а другой нет.
Стоит попробовать, если хочешь:
• удалёнку без опыта;
• дополнительный доход;
• работу без продаж и звонков;
• свободный график работы.
Новички без опыта обычно выходят на 50–80 тыс ₽ в месяц,
и за год легко вырастают до 200 тыс ₽ и выше.
Всё, что нужно для старта — запустить обучение
👉@pautov_techspec_bot
И пройти инструктаж.
А Алёна садится за ноутбук в своей уютной квартире на 3 часа, собирает бизнесу чат-бота в Тг, Вк или Максе из готовых блоков и получает 9-10 тыс на карту.
Разница не в таланте. Один знает, что чат-бота можно собрать на конструкторе как лего, а другой нет.
Стоит попробовать, если хочешь:
• удалёнку без опыта;
• дополнительный доход;
• работу без продаж и звонков;
• свободный график работы.
Новички без опыта обычно выходят на 50–80 тыс ₽ в месяц,
и за год легко вырастают до 200 тыс ₽ и выше.
Всё, что нужно для старта — запустить обучение
👉@pautov_techspec_bot
И пройти инструктаж.
👎1😁1
Event.composedPath() — определяем путь распространения события в DOM и Shadow DOM
Метод
Для обычного клика путь может включать исходный элемент, его предков,
Например, при нажатии на иконку внутри кнопки
Через
Метод хорош при работе с Shadow DOM. Из-за механизма retargeting внешний обработчик может получить в
Для клика внутри открытого Shadow DOM
Определение нажатий вне компонента — проверка пути события учитывает открытые границы Shadow DOM, которые обычный
Важно учитывать, что
🔥
📣 Code Ready | #практика
Метод
Event.composedPath() возвращает массив объектов, через которые проходит событие. В отличие от event.target, он позволяет получить весь доступный путь распространения, включая узлы открытого Shadow DOM:document.addEventListener('click', (event) => {
console.log('Target:', event.target);
console.log('Path:', event.composedPath());
});Для обычного клика путь может включать исходный элемент, его предков,
document и window. Это полезно при делегировании событий, особенно когда интерактивные элементы содержат вложенные узлы.Например, при нажатии на иконку внутри кнопки
event.target будет указывать на span, а не на саму кнопку:<button class="save-button">
<span class="icon">✓</span>
Save
</button>
Через
composedPath() можно найти кнопку независимо от того, на какой вложенный элемент пришёлся клик:document.addEventListener('click', (event) => {
const button = event.composedPath().find(
node =>
node instanceof Element &&
node.matches('.save-button')
);
if (button) {
console.log('Save:', button);
}
});Метод хорош при работе с Shadow DOM. Из-за механизма retargeting внешний обработчик может получить в
event.target хост компонента вместо внутреннего элемента:document.addEventListener('click', (event) => {
console.log(event.target);
console.log(event.composedPath()[0]);
});Для клика внутри открытого Shadow DOM
composedPath()[0] позволяет получить исходный внутренний узел. При этом закрытый Shadow DOM (mode: 'closed') не раскрывает свои внутренние элементы внешним обработчикам.Определение нажатий вне компонента — проверка пути события учитывает открытые границы Shadow DOM, которые обычный
contains() не пересекает:const panel = document.querySelector('.panel');
document.addEventListener('pointerdown', (event) => {
if (panel && !event.composedPath().includes(panel)) {
console.log('Pointer outside panel');
}
});Важно учитывать, что
composedPath() возвращает путь конкретного события, а не выполняет обход DOM-дерева. Его состав зависит от типа события и границ Shadow DOM.Event.composedPath() полезен для делегирования событий, работы с Web Components и корректной обработки взаимодействий через Shadow DOM.Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥7❤2🤝2
🏁Тестовый собес на Frontend с разработчиком из R-Vision, ex-Сбер
15 октября в 19:00 (МСК) приходи посмотреть открытое онлайн-собеседование на Frontend-разработчика!
Интервьюер: Даниил Дмитриев – Frontend-разработчик в R-Vision, ex-Сбер. Пишет на React и TypeScript, совмещает роль тимлида, менторит разработчиков и проводит код-ревью.
На эфире нет сценария ↓
Кандидат – случайный разработчик, который не знает вопросов заранее.
На обычном собесе ты не знаешь, что думает интервьюер, пока не придёт фидбек. На этом эфире Даниил проговорит всё вслух:
А в конце ответит на твои вопросы.
Эфир проходит в рамках менторской программы от ШОРТКАТ для разработчиков, которые хотят повысить грейд, ЗП и прокачать скиллы!
Участие бесплатное 🔥
Зарегаться на эфир можно в боте → @shortcut_front_bot
Реклама.
О рекламодателе.
15 октября в 19:00 (МСК) приходи посмотреть открытое онлайн-собеседование на Frontend-разработчика!
Интервьюер: Даниил Дмитриев – Frontend-разработчик в R-Vision, ex-Сбер. Пишет на React и TypeScript, совмещает роль тимлида, менторит разработчиков и проводит код-ревью.
На эфире нет сценария ↓
Кандидат – случайный разработчик, который не знает вопросов заранее.
На обычном собесе ты не знаешь, что думает интервьюер, пока не придёт фидбек. На этом эфире Даниил проговорит всё вслух:
— какие вопросы и задачи даёт кандидатам на Frontend-собесах;
— что в каждом ответе засчитал бы, а что нет — и почему.
А в конце ответит на твои вопросы.
Эфир проходит в рамках менторской программы от ШОРТКАТ для разработчиков, которые хотят повысить грейд, ЗП и прокачать скиллы!
Участие бесплатное 🔥
Зарегаться на эфир можно в боте → @shortcut_front_bot
Реклама.
О рекламодателе.
Как отключить целую часть интерфейса одной строкой HTML?
Часто для этого используют CSS.
Но
В HTML есть атрибут
Он делает всю область неактивной для обычного пользовательского взаимодействия: элементы не получают фокус, не активируются мышью или клавиатурой и исключаются из дерева доступности.
При этом не нужно отдельно отключать каждую кнопку.
Важно:
🔥 Это полезно для временно заблокированных разделов, неактивных панелей и частей интерфейса, с которыми пользователь пока не должен взаимодействовать.
📣 Code Ready | #совет
Часто для этого используют CSS.
.panel {
pointer-events: none;
}Но
pointer-events: none не запрещает переходить к элементам через Tab. Кнопки и поля могут оставаться доступными с клавиатуры.В HTML есть атрибут
inert.<section class="panel" inert>
<button>Сохранить</button>
<input placeholder="Название">
</section>
Он делает всю область неактивной для обычного пользовательского взаимодействия: элементы не получают фокус, не активируются мышью или клавиатурой и исключаются из дерева доступности.
При этом не нужно отдельно отключать каждую кнопку.
<button disabled>Сохранить</button>
<input disabled>
inert действует сразу на всё содержимое контейнера, включая вложенные элементы.Важно:
inert не скрывает элементы визуально и не заменяет проверку прав доступа.Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🔥6🤝2❤1