Другие полезные Web APIs
Современный браузер предоставляет десятки специализированных API; знание IntersectionObserver, ResizeObserver, MutationObserver и BroadcastChannel проверяется в вопросах о производительности — они заменяют дорогие паттерны вроде scroll-polling, setInterval-heartbeat и межвкладочного localStorage-хака.
// IntersectionObserver: lazy-load, infinite scroll, аналитика видимостиconst io = new IntersectionObserver((entries) => { for (const { target, isIntersecting } of entries) { if (isIntersecting) { target.src = target.dataset.src; // lazy load io.unobserve(target); // отписка после загрузки } }}, { root: null, // viewport как root rootMargin: '200px 0px', // preload за 200px до края threshold: 0.1, // срабатывать при 10% видимости});document.querySelectorAll('img[data-src]').forEach(img => io.observe(img));io.disconnect(); // отписать всех
// ResizeObserver: реакция на изменение размера элемента (не window)const ro = new ResizeObserver(entries => { for (const { target, contentRect, borderBoxSize } of entries) { // contentRect: content-box; borderBoxSize[0].inlineSize — border-box ширина target.style.fontSize = `${Math.max(12, contentRect.width / 20)}px`; }});ro.observe(document.querySelector('.fluid-text'));// MutationObserver: наблюдение за изменениями DOMconst mo = new MutationObserver(mutations => { for (const m of mutations) { if (m.type === 'childList') { m.addedNodes.forEach(n => n.nodeType === 1 && console.log('added:', n.tagName)); m.removedNodes.forEach(n => console.log('removed:', n)); } if (m.type === 'attributes') { console.log(`${m.attributeName} changed on`, m.target, '←', m.oldValue); } }});mo.observe(document.body, { childList: true, // добавление/удаление дочерних subtree: true, // всё поддерево (не только прямые дети) attributes: true, attributeFilter: ['class', 'data-state'], attributeOldValue: true,});// mo.takeRecords() — синхронно получить накопленные мутации до срабатывания callbackmo.disconnect();
// Clipboard API (требует user gesture / разрешения)await navigator.clipboard.writeText('copied to clipboard!');const text = await navigator.clipboard.readText(); // требует permissions// BroadcastChannel: pub/sub между вкладками без SharedWorkerconst ch = new BroadcastChannel('app-events');ch.postMessage({ type: 'THEME_CHANGED', value: 'dark' });ch.addEventListener('message', e => { if (e.data.type === 'LOGOUT') location.href = '/login';});ch.close(); // явное закрытие
// Screen Wake Lock: не гасить экран (видео-плееры, рецепты)let lock;async function keepScreenOn() { lock = await navigator.wakeLock.request('screen');}document.addEventListener('visibilitychange', async () => { if (document.visibilityState === 'visible' && lock?.released) { lock = await navigator.wakeLock.request('screen'); }});
// Web Notificationsconst perm = await Notification.requestPermission();if (perm === 'granted') { new Notification('Сборка завершена', { body: 'Build succeeded in 4.2s', icon: '/icon.png' });}
// navigator.onLine / offline eventwindow.addEventListener('offline', () => showBanner('No connection'));window.addEventListener('online', () => hideBanner());Итог: Observer API (Intersection, Resize, Mutation) работают асинхронно без блокировки main thread; BroadcastChannel — простейший pub/sub между вкладками без SharedWorker.