Как с помощью JavaScript определить пустые вкладки браузера
Установление соединений WebSocket, загрузка видео или изображения могут значительно снижать производительность сайта. Поэтому сокращение количества сетевых запросов может снизить затраты вебмастера на хостинг.
Page Visibility API
Современные браузеры имеют встроенный API, который позволяет определять, когда вкладка браузера скрыта (неактивна). Также можно зарегистрировать прослушиватель событий при изменении видимости вкладки.
document.visibilityState
document.visibilityState получает значение visible, когда страница находится на активной вкладке несвернутого окна. Либо hidden, когда страница не видна пользователю.
Можно напрямую получить доступ к document.visibilityState.
console.log(document.visibilityState);
// => Может быть `visible` или `hidden`
Событие visibilitychange
Также можно отследить изменения свойства видимости, используя прослушиватель событий.
const onVisibilityChange = () => {
if (document.visibilityState === 'hidden') {
console.log('> The window is hidden.');
} else {
console.log('> The window is visible.');
}
};
document.addEventListener('visibilitychange', onVisibilityChange, false);
Пример с использованием Polling
Рассмотрим сценарий, в котором мы запрашиваем API о наличии обновлений. При этом нужно избежать ненужных вызовов для неактивных пользователей. Упрощенный пример реализации:
const poll = () => {
const interval = 1500;
let _poller = null;
const repeat = () => {
console.log(`~ Polling: ${Date.now()}.`);
};
return {
start: () => {
_poller = setInterval(repeat, interval);
},
stop: () => {
console.log('~ Poller stopped.');
clearInterval(_poller);
}
};
};
const poller = poll();
poller.start();
const onVisibilityChange = () => {
if (document.visibilityState === 'hidden') {
poller.stop();
} else {
poller.start();
}
};
document.addEventListener('visibilitychange', onVisibilityChange, false);
Асинхронная загрузка в фоновом режиме
Вместо отмены всех задач можно асинхронно загружать внешние зависимости или ресурсы. Благодаря этому повысится UX сайта.
Webpack
Используя динамический импорт вместе с Webpack, можно загружать дополнительные модули или ресурсы в фоновом режиме.
let loaded = false;
const onVisibilityChange = () => {
if (document.visibilityState === 'hidden') {
// Предварительно загружаем внешние ресурсы и скрипты
if (loaded) {
return;
}
Promise.all([
import('./async.js'),
import('./another-async.js'),
import(/* webpackChunkName: "bar-module" */ 'modules/bar'),
import(/* webpackPrefetch: 0 */ 'assets/images/foo.jpg')
]).then(() => {
loaded = true;
});
}
};
document.addEventListener('visibilitychange', onVisibilityChange, false);
Rollup
Сборщик приложений Rollup также поддерживает динамический импорт.
let loaded = false;
const onVisibilityChange = () => {
if (document.visibilityState === 'hidden') {
// Предварительно загружаем внешние ресурсы и скрипты
if (loaded) {
return;
}
Promise.all([
import('./modules.js').then(({default: DefaultExport, NamedExport}) => {
// делаем что-то с модулями.
})
]).then(() => {
loaded = true;
});
}
};
document.addEventListener('visibilitychange', onVisibilityChange, false);
Предварительная загрузка с помощью JavaScript
Также можно предварительно загружать статические ресурсы, используя всего несколько строк JavaScript-кода.
let loaded = false;
const preloadImgs = (...imgs) => {
const images = [];
imgs.map(
url =>
new Promise((resolve, reject) => {
images[i] = new Image();
images[i].src = url;
img.onload = () => resolve();
img.onerror = () => reject();
})
);
};
const onVisibilityChange = () => {
if (document.visibilityState === 'hidden') {
// Предварительно загружаем внешние ресурсы и скрипты
if (loaded) {
return;
}
Promise.all(
preloadImgs(
'https://example.com/foo.jpg',
'https://example.com/qux.jpg',
'https://example.com/bar.jpg'
)
)
.then(() => {
loaded = true;
})
.catch(() => {
console.log('> Snap.');
});
}
};
document.addEventListener('visibilitychange', onVisibilityChange, false);
Микровзаимодействия
Действенный подход для привлечения внимания пользователей — это динамическое изменение иконки.
const onVisibilityChange = () => {
const favicon = document.querySelector('[rel="shortcut icon"]');
if (document.visibilityState === 'hidden') {
favicon.href = '/come-back.png';
} else {
favicon.href = '/example.png';
}
};
document.addEventListener('visibilitychange', onVisibilityChange, false);