navigator.sendBeacon для отправки данных при закрытии страницы!
Если нужно отправить аналитику или финальные данные сессии, обычные HTTP-запросы могут не успеть выполниться — пользователь закрыл вкладку или перешёл на другую страницу. Для таких случаев есть
Метод ставит данные в очередь на отправку и не блокирует закрытие страницы.
Базовый пример:
Метод принимает URL и данные. Запрос отправляется методом POST.
Если нужно явно указать Content-Type: application/json, лучше передать Blob:
Пример 1 — отправка при скрытии страницы:
Пример 2 — отправка
Можно отправлять строки,
Пример 3 — проверка:
Метод возвращает true, если браузер принял данные в очередь на отправку. Это не гарантия доставки, но означает, что отправка была запланирована браузером.
🔥 Если нужен ответ сервера, кастомные заголовки или полный контроль над запросом, можно использовать
📣 JS Ready | #практика
Если нужно отправить аналитику или финальные данные сессии, обычные HTTP-запросы могут не успеть выполниться — пользователь закрыл вкладку или перешёл на другую страницу. Для таких случаев есть
navigator.sendBeacon.Метод ставит данные в очередь на отправку и не блокирует закрытие страницы.
Базовый пример:
navigator.sendBeacon('/analytics', JSON.stringify({
event: 'page_close'
}));Метод принимает URL и данные. Запрос отправляется методом POST.
Если нужно явно указать Content-Type: application/json, лучше передать Blob:
navigator.sendBeacon(
'/analytics',
new Blob(
[JSON.stringify({ event: 'page_close' })],
{ type: 'application/json' }
)
);
Пример 1 — отправка при скрытии страницы:
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
navigator.sendBeacon('/analytics', JSON.stringify({
event: 'session_end',
time: Date.now()
}));
}
});visibilitychange часто используют для отправки последних метрик перед уходом пользователя. Обычно это работает стабильнее, чем beforeunload.Пример 2 — отправка
FormData:const data = new FormData();
data.append('event', 'scroll_depth');
data.append('value', 80);
navigator.sendBeacon('/analytics', data);
Можно отправлять строки,
FormData, Blob, URLSearchParams и некоторые бинарные типы данных.Пример 3 — проверка:
const accepted = navigator.sendBeacon('/analytics', JSON.stringify({
event: 'leave'
}));
console.log('Beacon accepted:', accepted);Метод возвращает true, если браузер принял данные в очередь на отправку. Это не гарантия доставки, но означает, что отправка была запланирована браузером.
fetch с опцией keepalive.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍10🤝8
Определяем готовность DOM через DOMContentLoaded!
Подписка на событие:
Это стандартная точка старта клиентской логики, когда элементы уже существуют в документе и с ними можно безопасно работать.
Событие
Используйте
Если скрипт подключается динамически или выполняется после загрузки документа, событие
Такой паттерн гарантирует корректный запуск независимо от момента подключения скрипта.
🔥
📣 JS Ready | #практика
DOMContentLoaded срабатывает после разбора HTML и построения DOM — можно безопасно запускать клиентский код, не дожидаясь полной загрузки страницы. Не ждёт изображения и другие ресурсы, но может задерживаться блокирующими CSS и синхронными скриптами.Подписка на событие:
document.addEventListener('DOMContentLoaded', () => {
console.log('DOM готов');
});Это стандартная точка старта клиентской логики, когда элементы уже существуют в документе и с ними можно безопасно работать.
Событие
load на объекте window срабатывает позже — после загрузки всех зависимых ресурсов страницы (изображений, стилей, шрифтов, iframe и т.д.):window.addEventListener('load', () => {
console.log('Страница полностью загружена');
});Используйте
load только когда действительно необходимы уже загруженные ресурсы (например, для получения фактических размеров изображений).Если скрипт подключается динамически или выполняется после загрузки документа, событие
DOMContentLoaded может уже произойти. В этом случае следует проверить document.readyState:if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
function init() {
console.log('Инициализация');
}Такой паттерн гарантирует корректный запуск независимо от момента подключения скрипта.
DOMContentLoaded срабатывает один раз за жизненный цикл документа и является базовой точкой запуска клиентского кода сразу после готовности структуры страницы.Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🔥9🤝8
This media is not supported in your browser
VIEW IN TELEGRAM
doka guide — сайт, на котором собраны шпаргалки по HTML, CSS, JS. На каждую тему есть подробный разбор с полезными советами по применению.
📌 Ссылочка: doka.guide
📣 JS Ready | #сайт
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14👍8🤝7❤2
Последовательные async-операции без лишней вложенности!
Когда несколько асинхронных действий зависят друг от друга, часто пишут вложенные
Такой код быстро превращается в избыточную вложенность обработчиков.
Решение — возвращать следующий Promise из
Каждый
Ошибки ловятся централизованно в
Важно:
🔥 Такой паттерн делает асинхронный код линейным, убирает лишнюю вложенность и упрощает поддержку.
📣 JS Ready | #совет
Когда несколько асинхронных действий зависят друг от друга, часто пишут вложенные
.then():fetch('/data').then(r => {
r.json().then(data => {
fetch(`/more/${data.id}`).then(r2 => {
r2.json().then(console.log);
});
});
});Такой код быстро превращается в избыточную вложенность обработчиков.
Решение — возвращать следующий Promise из
.then():fetch('/data')
.then(r => r.json())
.then(data => fetch(`/more/${data.id}`))Каждый
.then() получает результат предыдущего и передаёт дальше по цепочке, потому что возвращает Promise:.then(r => r.json())
.then(console.log)
Ошибки ловятся централизованно в
.catch(), если они произошли в цепочке или внутри .then():.catch(console.error);
Важно:
fetch не кидает ошибку на HTTP 4xx/5xx — в реальном коде проверяйте r.ok.Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥8🤝8
This media is not supported in your browser
VIEW IN TELEGRAM
Здесь собраны основные конструкции JS с готовыми примерами кода. Переменные, циклы, условия, массивы, строки, события, работа с датами и даже регулярные выражения. Главная фишка — это интерактивность: можно сразу копировать код, скрывать комментарии и использовать сайт как быстрый справочник во время работы.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥17👍12🤝8
Как убрать поле из объекта и не сломать исходные данные!
Иногда нужно убрать из объекта что-то лишнее перед отправкой данных в API. Классический пример — password.
Первое, что приходит в голову —
Работает, но есть нюанс:
Обычно безопаснее собрать новый объект без ненужного поля:
Здесь:
Если полей несколько, подход такой же:
На практике это часто пригождается в таких местах:
🔥 Ещё один момент: это не удаление в прямом смысле. Мы не убираем свойство из исходного объекта, а создаём новый — уже без него. Мелочь, но формулировка важная.
📣 JS Ready | #практика
Иногда нужно убрать из объекта что-то лишнее перед отправкой данных в API. Классический пример — password.
Первое, что приходит в голову —
delete:const user = {
name: "Alex",
age: 30,
password: "secret"
};
delete user.password;
console.log(user);Работает, но есть нюанс:
delete меняет сам объект. Если этот user уже используется где-то ещё, можно словить неприятный побочный эффект.Обычно безопаснее собрать новый объект без ненужного поля:
const user = {
name: "Alex",
age: 30,
password: "secret"
};
const { password, ...safeUser } = user;
console.log(safeUser);Здесь:
password просто забрали отдельно; safeUser — новый объект без этого поля; исходный user остался как был.Если полей несколько, подход такой же:
const user = {
name: "Alex",
age: 30,
password: "secret",
token: "abc123"
};
const { password, token, ...safeUser } = user;На практике это часто пригождается в таких местах:
function prepareUser(user) {
const { password, ...rest } = user;
return rest;
}Please open Telegram to view this post
VIEW IN TELEGRAM
👍17❤9🤝7🔥1
This media is not supported in your browser
VIEW IN TELEGRAM
ES7+ React/Redux/React-Native snippets — добавляет готовые сниппеты для компонентов, хуков, Redux-логики и других часто используемых конструкций. Достаточно ввести короткое сокращение и получаешь полноценный шаблон с импортами и структурой. Помогает быстрее создавать файлы, избегая рутины.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥16❤11👍9
Доступ к вложенным свойствам по строке!
Иногда путь к данным приходит динамически: из конфигурации, формы или API. Писать доступ вручную в таких случаях невозможно.
Если ключ хранится строкой, можно превратить его в универсальный доступ через
Каждый шаг берёт текущее значение и читает следующий ключ из пути:
А
Если на каком-то уровне свойства нет,
Подход работает для простых путей через точку (a.b.c).
🔥 Это пригодится для dynamic access, form builders, JSON-парсеров, таблиц, конфигов и любых систем, где путь к данным неизвестен заранее.
📣 JS Ready | #совет
Иногда путь к данным приходит динамически: из конфигурации, формы или API. Писать доступ вручную в таких случаях невозможно.
Если ключ хранится строкой, можно превратить его в универсальный доступ через
split + reduce:path.split('.').reduce((o, k) => o?.[k], obj);Каждый шаг берёт текущее значение и читает следующий ключ из пути:
'profile.name'.split('.')
// ['profile', 'name']А
reduce проходит по этому пути и безопасно читает значения:(o, k) => o?.[k]
Если на каком-то уровне свойства нет,
optional chaining вернёт undefined без ошибки.Подход работает для простых путей через точку (a.b.c).
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤8🔥7🤝2
Например, array.push() изменяет исходный массив,
а [...array, item] — создаёт новый без мутации. То же самое с sort(), reverse() и другими — у них есть безопасные аналоги вроде toSorted() и toReversed().На картинке — 9 мутирующих методов и их иммутабельные альтернативы, которые стоит держать под рукой при работе с массивами.
Сохрани, чтобы не забыть!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥16❤10👍8
This media is not supported in your browser
VIEW IN TELEGRAM
Репозиторий с огромной подборкой вопросов от базовых тем до продвинутых концепций. Собраны сотни вопросов по замыканиям, this, event loop, промисам, прототипам, асинхронности и др., которые регулярно встречаются на собеседованиях. Формат простой и удобный: вопрос, ответ, краткое объяснение.
Оставляю ссылочку: GitHub📱
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17❤8🔥8
closest(): поиск ближайшего элемента по селектору!
Иногда в обработчике клика вообще не важно, куда именно попали — в текст, иконку или вложенный span. Важно — к какому элементу это всё относится. В таких случаях обычно выручает
Он берёт текущий элемент (
Самый простой пример:
Если клик был внутри кнопки — получим её. Если нет — вернётся null.
Чаще всего это используют в делегировании событий. Например, список:
Без
Хорошо видно на вложенных элементах:
Клик может прийтись в svg, span, что угодно внутри кнопки — логика от этого не ломается.
Иногда добавляют ещё проверку на контейнер:
Это не всегда обязательно, но полезно, если одинаковые селекторы встречаются в разных частях страницы.
Ещё момент, который иногда забывают: если элемент уже подходит под селектор,
🔥 Очевидное, но всё же: селектор должен быть валидным, иначе будет ошибка. В итоге — небольшая штука, но сильно упрощает жизнь, когда работаешь с событиями и вложенной разметкой.
📣 JS Ready | #практика
Иногда в обработчике клика вообще не важно, куда именно попали — в текст, иконку или вложенный span. Важно — к какому элементу это всё относится. В таких случаях обычно выручает
Element.closest().Он берёт текущий элемент (
event.target) и поднимается вверх по DOM, пока не найдёт первый узел, который подходит под CSS-селектор. Причём проверяет и сам элемент тоже.Самый простой пример:
const button = event.target.closest('.btn');Если клик был внутри кнопки — получим её. Если нет — вернётся null.
Чаще всего это используют в делегировании событий. Например, список:
document.addEventListener('click', (e) => {
const item = e.target.closest('.list-item');
if (!item) return;
console.log('clicked:', item.dataset.id);
});Без
closest() здесь обычно начинается проверка e.target, потом parentElement, потом ещё выше — и код быстро превращается в цепочку условий. С closest() это одна строка.Хорошо видно на вложенных элементах:
document.addEventListener('click', (e) => {
const button = e.target.closest('button[data-action]');
if (!button) return;
handleAction(button.dataset.action);
});Клик может прийтись в svg, span, что угодно внутри кнопки — логика от этого не ломается.
Иногда добавляют ещё проверку на контейнер:
const container = document.querySelector('.container');
container.addEventListener('click', (e) => {
const item = e.target.closest('.item');
if (!item || !container.contains(item)) return;
console.log('inside container');
});Это не всегда обязательно, но полезно, если одинаковые селекторы встречаются в разных частях страницы.
Ещё момент, который иногда забывают: если элемент уже подходит под селектор,
closest() вернёт его же.const el = document.querySelector('.card');
console.log(el.closest('.card') === el); // truePlease open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤7👍7🤝4
В ней подробно показано:
• Как добавить спрайт-анимацию, фон и персонажей;
• Как реализовать внутриигровые эффекты и улучшения;
• Как анимировать столкновения, вращающиеся элементы, взрывы и эффекты отскока объектов;
• Как адаптировать игру под мобильные устройства и управлять производительностью Canvas.🔊 Продолжить можно на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
🤝12❤9👍8