Как с помощью 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);