Общение окон с разных доменов: postMessage

Интерфейс postMessage позволяет общаться друг с другом окнам и ифреймам с разных доменов.

Он очень удобен, например, для взаимодействия внешних виджетов и сервисов, подключённых через ифрейм с основной страницей.

Отправитель: метод postMessage

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

Проще говоря, если мы хотим отправить сообщение в окно win, то нужно вызвать win.postMessage(data, targetOrigin).

Аргументы:

data

Данные. По спецификации, это может быть любой объект, который будет клонирован с сохранением структуры при передаче.

Но IE поддерживает только строки, поэтому обычно данные JSON-сериализуют.

targetOrigin

Разрешить получение сообщения только окнам с данного источника.

Мы ведь не можем из JavaScript узнать, на каком именно URL находится другое окно. Но иногда хочется быть уверенным, что данные передаются в доверенный документ. Для этого и нужен этот параметр. Проверку осуществляет браузер. При указании '*' ограничений нет.

Например:

<iframe src="http://target.com" name="target">

<script>
  var win = window.frames.target;
  win.postMessage("сообщение", "http://javascript.ru");
</script>
В IE11- можно использовать postMessage только для ифреймов

В браузере IE, интерфейс postMessage работает только с ифреймами. Он не работает между табами и окнами.

Это ошибка в данном конкретном браузере, в других – всё в порядке. Детали по этой и связанным с ней ошибкам: HTML5 Implementation Issues in IE8 and later.

Получатель: событие onmessage

Чтобы получить сообщение, окно должно поставить обработчик на событие onmessage.

Свойства объекта события:

data
Присланные данные
origin
Источник, из которого пришло сообщение, например http://javascript.ru.
source
Ссылка на окно, с которого пришло сообщение. Можно тут же ответить.

Назначать обработчик нужно обязательно через методы addEventListener/attachEvent, например:

function listener(event) {
  if (event.origin != 'http://javascript.ru') {
    // что-то прислали с неизвестного домена - проигнорируем..
    return;
  }

  alert( "получено: " + event.data );
}

if (window.addEventListener) {
  window.addEventListener("message", listener);
} else {
  // IE8
  window.attachEvent("onmessage", listener);
}
Задержка отсутствуют

Задержки между отправкой и получением нет, совсем.

Если для setTimeout стандарт предусматривает минимальную задержку 4 мс, то для postMessage она равна 0 мс. Поэтому postMessage можно, в том числе, использовать как мгновенную альтернативу setTimeout.

Итого

Интерфейс postMessage позволяет общаться окнам и ифреймам с разных доменов (в IE8 – только ифреймы), при этом обеспечивая проверки безопасности.

  1. Отправитель вызывает targetWin.postMessage(data, targetOrigin).
  2. Если targetOrigin не '*', то браузер проверяет, совпадает ли источник с targetWin.
  3. Если совпадает, то на targetWin генерируется событие onmessage, в котором передаются:
  • origin – источник, с которого пришло сообщение.
  • source – ссылка на окно-отправитель.
  • data – данные. Везде, кроме IE, допустимы объекты, которые клонируются, а в IE – только строка.
  1. Обработчик на onmessage необходимо вешать при помощи специализированных методов addEventListener/attachEvent.
Карта учебника

Комментарии

перед тем как писать…
  • Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
  • Для одной строки кода используйте тег <code>, для нескольких строк кода — тег <pre>, если больше 10 строк — ссылку на песочницу (plnkr, JSBin, codepen…)
  • Если что-то непонятно в статье — пишите, что именно и с какого места.