Общение между вкладками браузера javascript

Обновлено: 06.07.2024

Я искал способ связи между несколькими вкладками или окнами в браузере (в одном домене, а не в CORS), не оставляя следов. Было несколько решений:

Первое, вероятно, наихудшее решение - вам нужно открыть окно из текущего окна, и тогда вы сможете общаться только при условии, что окна открыты. Если вы перезагрузите страницу в любом из окон, вы, скорее всего, потеряли связь.

Второй подход, использующий postMessage, возможно, обеспечивает связь между источниками, но сталкивается с той же проблемой, что и первый подход. Вам нужно поддерживать объект окна.

Четвертое решение, использующее localStorage, казалось, преодолевает ограничения файлов cookie, и его можно даже прослушивать, используя события. Как использовать это описано в принятом ответе.

Изменить 2018 год. Для этой цели лучше использовать BroadcastChannel, см. другие ответы ниже. Тем не менее, если вы все еще предпочитаете использовать localalstorage для связи между вкладками, сделайте это следующим образом:

Я создал библиотеку sysend.js , она очень маленькая, вы можете проверить ее источник код. У библиотеки нет внешних зависимостей.

Вы можете использовать его для связи между вкладками / окнами в одном браузере и домене. Библиотека использует BroadcastChannel, если поддерживается, или событие хранения из localStorage.

API очень прост:

Когда ваш браузер поддерживает BroadcastChannel, он отправляет буквальный объект (но на самом деле он автоматически сериализуется браузером), а если нет, то он сначала сериализуется в JSON и десериализуется на другом конце.

Последние версии также имеют вспомогательный API для создания прокси для междоменной связи. (требуется один HTML-файл на целевом домене).

Новая версия также поддерживает междоменную связь, если вы включили специальный файл proxy.html в целевой домен и вызвали функцию proxy из исходного домена:

(proxy.html это очень простой HTML-файл, в котором есть только один скрипт-тег с библиотекой).

ПРИМЕЧАНИЕ . Если вы реализуете те же функции с помощью localStorage, в IE есть проблема. Событие хранения отправляется в то же окно, которое вызвало событие, а для других браузеров оно вызывается только для других вкладок / окон.

Для этой цели существует современный API - Канал вещания

Это так же просто, как:

Вероятно, кроме чистоты API, главное преимущество этого API - отсутствие строковой классификации объектов.

В настоящее время поддерживается только в Chrome и Firefox, но вы можете найти полифилл, использующий localStorage.

Я создал модуль, который работает как официальный Broadcastchannel , но имеет запасные варианты, основанные на localalstorage, indexeddb и Unix- сокеты . Это гарантирует, что он всегда работает даже с Webworkers или NodeJS. См. pubkey: BroadcastChannel

Для тех, кто ищет решение, не основанное на jQuery, это простая версия JavaScript решения, предоставленная Томасом М:

Еще один метод, который люди должны рассмотреть, это Shared Workers. Я знаю, что это передовая концепция, но вы можете создать ретранслятор на Shared Worker, который НАМНОГО быстрее, чем localalstorage, и не требует отношения между родительским / дочерним окном, если вы находитесь в одном источнике.

См. Мой ответ здесь , где я мог обсудить это.

Используя библиотеку, которую я создал storageManager , вы можете добиться этого следующим образом:

Есть и другие удобные методы для обработки других сценариев.

Есть небольшой компонент с открытым исходным кодом для синхронизации / связи между вкладками / окнами одного и того же происхождения (отказ от ответственности - я один из авторов!), Основанный на localStorage .

Постскриптум Я позволил себе порекомендовать его здесь, так как большинство компонентов «lock / mutex / sync» не работают на соединениях веб-сокетов, когда события происходят почти одновременно

Политика «Одинакового источника» (Same Origin) ограничивает доступ окон и фреймов друг к другу.

Политика "Одинакового источника"

Два URL имеют «одинаковый источник» в том случае, если они имеют совпадающие протокол, домен и порт.

Эти URL имеют одинаковый источник:

А эти – разные источники:

Политика «Одинакового источника» говорит, что:

  • если у нас есть ссылка на другой объект window , например, на всплывающее окно, созданное с помощью window.open или на window из <iframe> и у этого окна тот же источник, то к нему будет полный доступ.
  • в противном случае, если у него другой источник, мы не сможем обращаться к его переменным, объекту document и так далее. Единственное исключение – объект location : его можно изменять (таким образом перенаправляя пользователя). Но нельзя читать location (нельзя узнать, где находится пользователь, чтобы не было никаких утечек информации).

Доступ к содержимому ифрейма

Внутри <iframe> находится по сути отдельное окно с собственными объектами document и window .

Мы можем обращаться к ним, используя свойства:

  • iframe.contentWindow ссылка на объект window внутри <iframe> .
  • iframe.contentDocument – ссылка на объект document внутри <iframe> , короткая запись для iframe.contentWindow.document .

Когда мы обращаемся к встроенному в ифрейм окну, браузер проверяет, имеет ли ифрейм тот же источник. Если это не так, тогда доступ будет запрещён (разрешена лишь запись в location , это исключение).

Для примера давайте попробуем чтение и запись в ифрейм с другим источником:

Код выше выведет ошибку для любых операций, кроме:

  • Получения ссылки на внутренний объект window из iframe.contentWindow
  • Изменения location .

С другой стороны, если у ифрейма тот же источник, то с ним можно делать всё, что угодно:

Событие iframe.onload – по сути то же, что и iframe.contentWindow.onload . Оно сработает, когда встроенное окно полностью загрузится со всеми ресурсами.

…Но iframe.onload всегда доступно извне ифрейма, в то время как доступ к iframe.contentWindow.onload разрешён только из окна с тем же источником.

Окна на поддоменах: document.domain

По определению, если у двух URL разный домен, то у них разный источник.

Для этого в каждом таком окне нужно запустить:

После этого они смогут взаимодействовать без ограничений. Ещё раз заметим, что это доступно только для страниц с одинаковым доменом второго уровня.

Ифрейм: подождите документ

Когда ифрейм – с того же источника, мы имеем доступ к документу в нём. Но есть подвох. Не связанный с кросс-доменными особенностями, но достаточно важный, чтобы о нём знать.

Когда ифрейм создан, в нём сразу есть документ. Но этот документ – другой, не тот, который в него будет загружен!

Так что если мы тут же сделаем что-то с этим документом, то наши изменения, скорее всего, пропадут.

Нам не следует работать с документом ещё не загруженного ифрейма, так как это не тот документ. Если мы поставим на него обработчики событий – они будут проигнорированы.

Как поймать момент, когда появится правильный документ?

Можно проверять через setInterval :

Коллекция window.frames

Другой способ получить объект window из <iframe> – забрать его из именованной коллекции window.frames :

  • По номеру: window.frames[0] – объект window для первого фрейма в документе.
  • По имени: window.frames.iframeName – объект window для фрейма со свойством name="iframeName" .

Ифрейм может иметь другие ифреймы внутри. Таким образом, объекты window создают иерархию.

Навигация по ним выглядит так:

  • window.frames – коллекция «дочерних» window (для вложенных фреймов).
  • window.parent – ссылка на «родительский» (внешний) window .
  • window.top – ссылка на самого верхнего родителя.

Можно использовать свойство top , чтобы проверять, открыт ли текущий документ внутри ифрейма или нет:

Атрибут ифрейма sandbox

Атрибут sandbox позволяет наложить ограничения на действия внутри <iframe> , чтобы предотвратить выполнение ненадёжного кода. Атрибут помещает ифрейм в «песочницу», отмечая его как имеющий другой источник и/или накладывая на него дополнительные ограничения.

Существует список «по умолчанию» ограничений, которые накладываются на <iframe sandbox src=". "> . Их можно уменьшить, если указать в атрибуте список исключений (специальными ключевыми словами), которые не нужно применять, например: <iframe sandbox="allow-forms allow-popups"> .

Другими словами, если у атрибута "sandbox" нет значения, то браузер применяет максимум ограничений, но через пробел можно указать те из них, которые мы не хотим применять.

Вот список ограничений:

allow-same-origin "sandbox" принудительно устанавливает «другой источник» для ифрейма. Другими словами, он заставляет браузер воспринимать iframe , как пришедший из другого источника, даже если src содержит тот же сайт. Со всеми сопутствующими ограничениями для скриптов. Эта опция отключает это ограничение. allow-top-navigation Позволяет ифрейму менять parent.location . allow-forms Позволяет отправлять формы из ифрейма. allow-scripts Позволяет запускать скрипты из ифрейма. allow-popups Позволяет открывать всплывающие окна из ифрейма с помощью window.open .

Больше опций можно найти в справочнике.

Пример ниже демонстрирует ифрейм, помещённый в песочницу со стандартным набором ограничений: <iframe sandbox src=". "> . На странице содержится JavaScript и форма.

Обратите внимание, что ничего не работает. Таким образом, набор ограничений по умолчанию очень строгий:

Мне захотелось наладить общение скриптов из разных вкладок браузера. Будущий API SharedWorker позволяет передавать данные между разными iframe и даже вкладками или окнами. В Chrome он работает давно, в Firefox – недавно, а в IE и Safari его не видать. Но существует кроссбраузерная альтернатива, о которой мало кто знает. Давайте разбираться.

Представьте, что на одной вкладке человек залогинился, затем открыл другую, и там разлогинился. На первой он вроде как залогинен, но когда он сделает там что-либо, ему выдадут ошибку. Хорошо было бы хотя бы показать ему диалог о том, что он разлогинился и ему надо войти ещё раз.

Можно было бы использовать API WebSocket, но это черезчур сложно. Я начал искать другие решения. Первое – сохранить куки и localStorage, и проверять их периодически. Но это нагружало бы процессор достаточно бесполезной задачей – ведь выхода могло и не случиться вообще. Меня больше устроили бы варианты с long-polling (длинные запросы), Server-Sent Events или WebSockets. Удивительно, но в результате оказалось, что ответ лежал в области localStorage!

Знаете ли вы, что localStorage запускают события? Точнее, событие возникает, когда нечто добавляется, меняется или удаляется из хранилища. Это значит, что когда вы касаетесь localStorage в любой вкладке, все остальные могут узнать об этом. Достаточно прослушивать события в объекте window.

У объекта event есть следующие свойства:

key – ключ, который трогали в localStorage
newValue – новое назначенное ему значение
oldValue – значение перед изменением
url – URL страницы, на которой случилось изменение

Поэтому можно наладить общение между вкладками, просто задавая значения в localStorage. Представьте следующий пример (псевдокод):

Когда пользователь выходит на одной из вкладок, и переходит на другую, страница перезагружается и серверная логика перенаправляет его куда-то. При этом проверка происходит только если пользователь выбрал эту вкладку. Если вдруг он вышел и вошёл на одной из вкладок, нет нужды разлогинивать его на всех остальных.

Это должно работать и в другую сторону – если пользователь залогинился на одной вкладке, а на другой он был разлогинен, то при переходе на вторую вкладку страница перезагрузится и он там тоже будет залогинен. Радость.

API попроще

localStorage API — один из самых простых интерфейсов. Однако и у него есть особенность – например Safari и QuotaExceededError, нет поддержки JSON и старых браузеров.

Для этого я сделал модуль, предоставляющий локальное хранилище с упрощённым API, избавляющий вас от указанных особенностей, работающий с памятью, если вдруг поддержки localStorage нет, и позволяющий проще работать с событиями. Можно регистрировать и удалять слушателей этих событий для заданных ключей.

Вот схема работы с local-storage.

Стоит упомянуть, что local-storage регистрирует один обработчик событий и отслеживает все ключи, за которыми вы наблюдаете, вместо регистрации множества событий.

Наверно можно придумать и другие случаи, когда возможно использовать общение между вкладками. Пока SharedWorker не получил должного распространения, а подход WebSockets ненадёжен, если ваше приложение ориентируется в первую очередь на работу оффлайн.

На протяжении многих лет возможности браузеров постоянно увеличивались в ответ на растущие потребности веб-приложений. И теперь у нас есть множество способов получения одной и той же (или схожей) функциональности. На такую особенность браузеров, как возможность обмена данными между вкладками, редко обращают внимание. Рассмотрим несколько сценариев, в которых она может потребоваться:

  • Изменение темы (например, тёмная или светлая тема) приложения распространяется на уже открытые вкладки браузера.
  • Получение последнего токена для аутентификации и использование его во всех вкладках браузера.
  • Синхронизация состояния приложения во всех вкладках браузера.

На момент написания этой статьи существовало несколько достойных внимания подходов, в соответствии с которыми осуществляется обмен данными между вкладками браузеров. У каждого из этих подходов свои сильные и слабые стороны, поэтому остановимся на них подробнее, чтобы вы могли найти подходящий для вас вариант.

1. «Events» («События») в локальном хранилище данных

Возможно, вы уже использовали LocalStorage, который доступен на разных вкладках в рамках одного и того же приложения-источника. Но знаете ли вы, что LocalStorage поддерживает события? Эту функцию можно использовать для обмена данными между вкладками браузера: хранилище обновляется, после чего событие получат другие вкладки.

Например, в одной вкладке выполняется следующий код JavaScript:

И другие, прослушивающие событие вкладки получат это событие:

Но здесь есть несколько ограничений:

  • Событие не срабатывает для вкладки, на которой выполняется действие по вводу значений в хранилище.
  • Этот подход имеет негативные последствия для большого объёма данных: из-за синхронности выполняемых в LocalStorage действий основной поток пользовательского интерфейса может быть заблокирован.

2. API широковещательного канала

API широковещательного канала позволяет осуществлять обмен данными между вкладками, окнами, фреймами, Iframes и веб-воркерами . Одна вкладка создаёт что-то и опубликовывает это на канале:

А другие вкладки прослушивают канал:

И таким образом происходит обмен данными между контекстами браузера ( окнами , вкладками , фреймами или Iframes ). Такой способ обмена данными между вкладками браузера очень удобен. Тем не менее safari и IE его не поддерживают. Более подробная информация содержится в разделе «BroadcastChannel» документации MDN .

Подсказка: выкладывайте на Bit ( Github ) переиспользуемые компоненты для своих проектов. Здесь очень просто размещать, документировать и организовывать независимые компоненты из любого проекта.

Задействуйте этот ресурс для максимально многократного использования кода и совместной работы над независимыми компонентами, а также для создания масштабируемых приложений.

Bit поддерживает Node, TypeScript, React, Vue, Angular и много других инструментов.

Пример: просмотр повторно используемых компонентов React, выложенных на Bit.dev Пример: просмотр повторно используемых компонентов React, выложенных на Bit.dev

navigator.serviceWorker.controller.postMessage( broadcast: data
>);

А принимающий воркер на другой вкладке браузера прослушивает это событие:

targetWindow .postMessage(message, targetOrigin)

А целевое окно прослушивает события:

Одно из преимуществ этого подхода перед другими — возможность поддержки обмена данными между разными источниками. Но есть и ограничение: необходима ссылка на другую вкладку браузера. Поэтому этот подход только для вкладок браузера, открытых через window.open() или document.open(). Более подробная информация содержится в документации MDN .

Заключение

Надеюсь, статья была для познавательной и рассмотренные в ней подходы будут полезны для ваших веб-приложений. Каждый подход уникален и имеет свои варианты использования.

Кроме этих четырёх подходов, для обмена данными между вкладками браузера и даже между устройствами в режиме реального времени применяются Websockets («веб-сокеты») и Server-Sent Events («события, посылаемые сервером»). Но для этого понадобится веб-сервер. А вот рассмотренные в статье подходы не зависят от веб-сервера, и их применение позволяет осуществлять обмен данными в браузере без него и делать это быстро.

Каков самый надежный способ взаимодействия Javascript между вкладками/windows одного и того же браузера? Например, когда Tab 2 начинает воспроизведение звука, Tab 1 каким-то образом знает об этом и может приостановить свой плеер.

Я создаю сайт с музыкальным плеером. таким образом, на данный момент, если вы откроете две вкладки на сайте, вы можете запустить музыку на обеих. Это явно плохо, поэтому я пытаюсь найти решение.

Есть идеи? Спасибо

Я хочу переключаться между 2 вкладками браузера. В настоящее время пытался получить количество оконных ручек, но даже при том, что 2 вкладки открыты, он показывает как 1.

Это старый ответ, я предлагаю использовать современную версию, описанную здесь:

Вы можете общаться между браузерами windows (и вкладками тоже) с помощью файлов cookie.

Вот пример отправителя и получателя:

В w2 вы можете получить доступ к w1 с помощью свойства открывания окна.

Очевидно, что это экспериментальная технология, и она пока не поддерживается во всех браузерах.

Вы можете сделать это через локальное хранилище API. Обратите внимание, что это работает только между 2 вкладками. вы не можете поместить отправителя и получателя на одну страницу:

На странице отправителя:

На странице получателя

Связь между различными контекстами выполнения JavaScript поддерживалась еще до HTML5, если документы были одного происхождения. Если нет или у вас нет ссылки на другой объект Window , то вы можете использовать новый postMessage API, введенный с HTML5. Я немного подробно остановился на обоих подходах в этом ответе на stackoverflow .

Вы можете общаться между windows (с вкладками или без), если у них есть отношения "ребенок-родитель".

Создание и обновление дочернего окна:

Дочерний windows может использовать объект parent для связи с родителем, который его породил, поэтому вы можете управлять музыкальным плеером из любого окна.

Нашел другой способ, используя HTML5 localstorage, я создал библиотеку с такими событиями, как API:

редактировать: С помощью Flash вы можете общаться между любым окном, ANY браузером (да, от FF до IE во время выполнения ) ..ANY форма экземпляра flash (ShockWave/activeX)

Похожие вопросы:

Я много искал о том, как общаться между фрагментами с помощью SlidingTabLayout, но на самом деле не получил хорошего ответа. Я знаю, что использую ActionBar, но я хотел новый способ, который.

Возможный Дубликат : Javascript связь между вкладками браузера/windows При условии, что мои ограничения - это возможности браузера и у меня есть две веб-страницы, можно ли написать javascript на.

Я хочу переключаться между 2 вкладками браузера. В настоящее время пытался получить количество оконных ручек, но даже при том, что 2 вкладки открыты, он показывает как 1.

Я искал способ, как общаться между несколькими вкладками или windows в браузере (в том же домене, а не CORS), не оставляя следов. Решений было несколько: использование объекта window postMessage.

Возможно ли иметь событийную связь между браузером tabs/windows? Я знал, что это может быть (по крайней мере теоретически) возможно с использованием локального хранилища . Не могли бы вы привести.

У меня есть две вкладки браузера, на которых запущены приложения мониторинга на экране. Я хочу автоматизировать переключение между вкладками, скажем, каждые 2 минуты. Я думаю, что это может быть.

Я видел много решений о том, как обмениваться данными между вкладками с помощью Redux/JavaScript с помощью localstorage. Нравится: import < applyMiddleware, createStore >from redux; import logger.

Читайте также: