search check home clock-o tag tags chevron-left chevron-right chevron-up chevron-down twitter facebook github rss comment comments terminal code

[HTML5] Jak komunikować się między stronami za pomocą HTML5 postMessage API

[HTML5] Jak komunikować się między stronami za pomocą HTML5 postMessage API

Specyfikacja HTML5 zawiera wiele różnych API. Jednym z nich jest API do komunikowania się stron umieszczonych w ramkach (iframe) ze stroną główną.
Tego typu komunikacja może być bardzo przydatna w różnego rodzaju aplikacjach internetowych i nie tylko, gdzie istnieje potrzeba komunikacji między zupełnie różnymi stronami znajdującymi się w różnych domenach.

W skrócie, window.postMessage API zachowuje się jak AJAX bez odwoływania się do backendu strony internetowej (na przykład do metod PHP czy też skryptów w node.js).

HTML5 postMessage API - strona źródłowa

Na początek należy przygotować stronę źródłową/główną, która będzie wysyłała wiadomości za pomocą postMessage API. Zaczniemy od kodu HTML:

1
2
3
4
5
6
7
8
9
<form id="login" action="/">
<ul>
  <li><label for="username">Nazwa użytkownika:</label><input type="text" name="login" id="username"></li>
  <li><label for="password">Hasło:</label><input type="password" name="pass" id="password"></li>
  <li><input type="submit" value="Zaloguj"></li>
</ul>
</form>
<iframe id="target" src="[adres-url-ramki]"></iframe>
<p id="answer"></p>

Przygotowano prosty formularz, ktory zawiera 2 pola do wypełnienia i przycisk wyślij. Do tak przygotowanego formularza zastosujemy następujący skrypt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  window.onload = function () {
    var iframe    = document.getElementById('target').contentWindow;
    var form      = document.getElementById('login');
    var domain    = 'http://demo.piotrnalepa.pl';
 
    form.onsubmit = function (event) {
      event.preventDefault();
 
      var data = {
        user : document.getElementById('username').value,
        pass : document.getElementById('password').value
      };
      iframe.postMessage(data, domain);
 
      event.returnValue = false;
      return false;
    };
 
    window.addEventListener('message', function (event) {
      if (event.origin !== domain) return;
 
      document.getElementById('answer').innerHTML = event.data;
    },false);
  };

Powyższy kod JS jest odpowiedzialny za przesłanie wiadomości do strony znajdującej się w ramce. Wykorzystałem tutaj czysty JS, który na pewno nie zadziała w IE8 i niżej (nie obsługują windows.addEventListener).

Po kliknięciu przycisku, następuje utworzenie obiektu z danymi podanymi przez użytkownika a następnie przesłanie tych danych do strony w ramce za pomocą iframe.postMessage, ktory przyjął 2 argumenty: dane i adres domeny docelowej, czyli adres strony w ramce. Dla strony źródłowej zdefiniowałem zachowanie się strony w momencie, gdy strona wywoła zdarzenie - message. Zdarzenie message jest jednym ze zdarzeń obsługiwanych przez JS, tak jak click, hover, mouseleave, itd. Gdy strona źródłowa otrzyma odpowiedź z ramki, to wyświetli jej treść w kontenerze o identyfikatorze #answer.

HTML5 postMessage API - strona docelowa/ramka

Teraz pora zająć się stroną docelową, ktora będzie przyjmowala dane z formularza na poprzedniej stronie i ewentualnie będzie mogła sobie je przetworzyć w razie potrzeby. Jej kod HTML wygląda następująco:

1
2
<h2>To jest strona na zupełnie innym serwerze.</h2>
<p id="message">A tu będzie przesłana wiadomość</p>

Nic specjalnego, nagłówek oraz paragraf z identyfikatorem. W tamtym paragrafie będzie wyświetlona wiadomość ze strony źródłowej.
Kod JS, który będzie obslugiwal otrzymywanie wiadomości ze strony źródłowej wygląda nastepująco:

1
2
3
4
5
6
7
8
9
10
window.addEventListener('message', function (event) {
  var message;
  if (event.origin !== 'https://blog.piotrnalepa.pl') {
    message = 'To nie jest wiadomość z bloga https://blog.piotrnalepa.pl<br/>, tylko z: ' + event.origin;
  } else {
    message = 'Oto dane jakie dostarczono: ' + JSON.stringify(event.data) + '<br/> z domeny: ' + event.origin;
	event.source.postMessage('Wiadomość otrzymano :)', event.origin);
  }
  document.getElementById('message').innerHTML = 'Wiadomość:<br/>' + message;
}, false);

Wywołano metodę, ktora nasłuchuje czy nie miało miejsce wydarzenie message, a jeśli tak to sprawdza czy adres strony źródlowej się zgadza z adresem zdefiniowanym w kodzie JS strony docelowej. To jest zabezpieczenie przed niepożądanymi zapytaniami z innych stron. W zależności od tego czy adres strony źródłowej się zgadza następują inne akcje (w tym przypadku, generowana jest inna wiadomość). Następnie, do kontenera o identyfikatorze #message wstawiana jest treść wiadomości, a na końcu następuje wysłanie wiadomości zwrotnej do strony źródłowej, że wiadomość otrzymano. Należy zwrócić uwagę, że adres strony źródłowej jest zawarty w event.source i to na nim wykonujemy akcję postMessage.

HTML5 postMessage API - podsumowanie

Jak widać, nie trzeba koniecznie wykonywać zapytań AJAX-owych aby otrzymać dane z innych stron. Można równie dobrze wykorzystać do tego postMessage API. Dzięki temu, cała akcja dzieje się po stronie frontendu, czyli przeglądarki użytkownika. W podstawowym założeniu, miało to na celu uniknięcie wykonywania zapytań AJAX do skyptów serwerowych, jeśli istniała możliwość przechowywania potrzebnych danych w wygenerowanej ramce.

Mam nadzieję, że będzie to przydatny tekst i pozwoli Ci to na stworzenie wielu ciekawych funkcjonalności w oparciu o tą technologię, która jest właściwie wspierana we wszystkich popularnych przeglądarkach.

Przeglądarkawsparcie
Mozilla Firefoxtak
Google Chrometak
Operanie
Safaritak
Internet Explorer

7/8/9/10

nie/tak/tak/tak

Demo z tego artykułu można zobaczyć tutaj: HTML5 postMessage demo

źródło

  • Comandeer

    AFAIK Facebook wykorzystuje to we wszystkich swoich widgetach do ustalania rozmiaru ramek. I to chyba jest najczęstszy powód stosowania postMessage 😉 Disqus chyba zaczyna eksperymentować z iframe[seamless], żeby nie musieć robić takiego bzdurnego JS-owego hacku
    No i wydaje mi się, że podstawowym założeniem było jednak ominięcie same origin policy. nie da się dostać do ramki, ale można jej wysłać wiadomość. ot, taka zamierzona luka bezpieczeństwa 😉
    I tak już na koniec, co to się dawno nie czepiałem: w event.source raczej jest referencja do ramki a nie jej adres 😛

  • W event.source zawsze jest przesyłany adres URL strony, która wysłała zdarzenie message.