Наглядный пример задачи — сайт vk.com. Каждый раз когда вы воспроизводите музыку или видео в одной вкладке — в других вкладках воспроизведение останавливается. И если вы обратитесь в интернет за помощью в решении данной задачи, то наверняка найдете описание Storage Events или Page Visibility API или даже готовые решения, к примеру Visibility.js.
На хабре уже был обзор этих вещей, к примеру вот и ещё.
Я прошел по этому пути, но не одно из решений меня не устроило. Мне хотелось чего-нибудь простого, легковесного и не требующего глубокого изучения деталей.
Решение, которое получилось в итоге, обрело название DuelJS (просто рандомное уникальное название) и ниже я попробую сравнить его с Visibility.js, дабы избежать лишней критики в свою сторону.
Активная вкладка
Удобная кроссбраузерная обертка для простого отражения состояния «эта вкладка сейчас активна», в visibility.js будет выглядеть следующим образом:
if ('visible' == Visibility.state()) {
// эта вкладка активна
}
У вкладки Visibility есть 3 состояния: visible, hidden и prerender.
Преимуществом Visibility также являются callbacks, которые вы можете вешать на множество событий, таких как активизация вкладки, setInterval в активном окне и тому подобное.
Философия DuelJS слегка упрощена:
1. Все вкладки имеют лишь 2 состояния — Master и Slave
2. Мастер вкладка это вкладка в которой ведется работа — ничего лишнего, все остальные есть Slave.
При таком подходе достаточно лишь одной функции: window.isMaster() — проверить является ли вкладка мастером.
if (window.isMaster()) {
// эта вкладка активна
}
Коммуникации между вкладками
Теперь перейдем к коммуникациям между вкладками. Наиболее подходящим решением мне показалось использование Storage Events, хотя они и не без проблем. К слову находил я в гугле еще такие варианты как использование postMessage API или WebSockets.
Основная проблема Storage Events заключается в их плохой поддержке некоторыми MSIE, хотя до недавнего времени и у других браузеров тоже могли возникать с этим проблемы.
Так как Visibility.js является по сути оберткой над Page Visibility API — работа со Storage Events в ней отсутствует.
В DuejJS существует кроссбраузерная обертка над Storage Events, которая выражается в следующей философии:
1. Коммуникации между вкладками осуществляются при помощи каналов
2. Внутри канала вкладки могут запускать события, на которые могут реагировать другие вкладки в этом канале
Создать канал достаточно просто:
var ch = duel.channel('channel_name'); // channel_name - имя канала
Теперь определим поведение при вызове события qwerty:
ch.on('qwerty', function (a, b, c, ...) {})
Метод on у канала определяет его поведение. В функции, передаваемой вторым параметром может быть сколько угодно аргументов, или не быть вовсе.
Запустить событие так же легко. Слово on теперь заменим на broadcast, а передаваемые аргументы вставим после названия события:
ch.broadcast('qwerty', a, b, c, ...)
Создание плеера с поведением как на vk.com
Для нетерпеливых читателей сразу даю ссылку на рабочий пример.
Основная суть приложения заключается в трех строках:
1. var player = duel.channel('player'); (определение канала)
2. player.on('stop', function () {… (определение поведения при событии stop)
3. player.broadcast('stop'); (запуск события stop)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Остановлено</title>
<!-- Заголовок (title) страницы отражает состояние воспроизведения. -->
<script type="text/javascript" src="duel.min.js"></script>
<link rel="stylesheet" type="text/css" href="pretty.css">
</head>
<body>
<!-- div #preview представляет из себя кликабельную картинку, при клике по которой инициализируется iframe с роликом youtube -->
<div id="preview"><div>PLAY</div></div>
<a href="index.html" target="_blank">Открыть новую вкладку</a>
<script type="text/javascript">
/** открываем канал с именем player */
var player = duel.channel('player');
/** превью для видео-ролика */
var previewDiv = document.getElementById('preview');
/**
* Регистрируем новое событие канала player
*/
player.on('stop', function () {
/**
* Удаляем iframe
*/
var frame = document.getElementsByTagName('iframe')[0];
frame.parentNode.removeChild(frame);
/**
* Показываем превью
*/
previewDiv.style.display = 'block';
/**
* Обновляем заголовок вкладки
*/
document.title = 'Остановлено';
});
previewDiv.onclick = function () {
/**
* Посылаем сигнал stop в канал player
*/
player.broadcast('stop');
/**
* Создаем новый элемент iframe с видео из youtube
*/
var frame = document.createElement("iframe");
frame.width = '859';
frame.height = '480';
frame.src = '//www.youtube.com/embed/xsV8TrF4gN0?rel=0&autoplay=1';
frame.frameborder = '0';
/**
* Встраиваем его под превью и скрываем превью
*/
previewDiv.parentNode.insertBefore(frame, previewDiv.nextSibling);
previewDiv.style.display = 'none';
/**
* Обновляем заголовок вкладки
*/
document.title = 'Играем...';
}
</script>
</body>
</html>
Заключение
Буду очень рад если вам поможет данное решение и вы используете его в своих проектах. Я открыт к идеям по улучшению. Либа — кроссбраузерная и работает в том числе и в IE, за счет встроенных хаков.
Полезные ссылки
Краткая документация DuelJS на сайте
Репозиторий DuelJS на GitHub
Ещё одна демка DuelJS
Автор: student_ivan