Share

window.postMessage API

Sử dụng Window.postMessage API để truyền dữ liệu giữa các iframe/popup qua lại với nhau và với trang chính.

Trong một project mình làm, vấn đề gặp phải là mình cần phải mở một popup windows để user login bằng facebook, nhưng trong poup này, có khá nhiều redirect, vì vậy không thể handle sự kiện close của popup này để update view trong trang chính được. Ngày hôm nay mình đã tìm ra giải pháp cho nó. Windows.postMessage API, một API của HTML5, và mình chỉ mới vừa biết về nó, không có nhiều người nói về nó.

Windows.postMessage cho phép các frame/windows khác domain có thể giao tiếp với nhau. Và ngày hôm nay chúng ta sẽ tìm hiểu về nó, để có giải pháp cho bài toán truyền dữ liệu giữa các frame/windows khác hoặc cùng domain.

Bạn có thể đọc những mô tả rất cụ thể về API này tại trang dưới đây :

Cú pháp

Cú pháp này có mô tả kỹ ở trang tài liệu phía trên, mình viết lại để cho tiện đọc bài này :

otherWindow.postMessage(message, targetOrigin, [transfer]);

Ý nghĩa của các tham số như sau :

Tên Mô tả
otherWindow Reference tới một windows hoặc iframe nào đó mà bạn muốn gửi message, giá trị này được trả về khi gọi hàm window.open hoặc tên, id của frame trong biến window.frames
message Dữ liệu cần gửi vào otherWindow
targetOrigin Origin mà trang otherWindows phải có nếu muốn nhận được message này. Orign này và của otherWindows phải khớp với nhau từ host, domain, port và path. Đây là tham số quan trọng để bảo vệ message của bạn khỏi một số windows khác cố tình nghe lén message. Bạn có thể sử dụng “*” tuy nhiên như vậy là bạn đang “hét lớn” message này cho mọi windows.

Gửi message

Như đã nói ở phần cú pháp phía trên, để gửi một message thì chúng ta cần phải biết otherWindows, message và targetOrigin. Tất nhiên rồi, dưới đây là một ví dụ nhỏ, chúng ta sẽ gửi message vào popup windows :

/*
 * In window A's scripts, with A being on <http://example.com:8080>:
 */

var popup = window.open(...popup details...);

// When the popup has fully loaded, if not blocked by a popup blocker:

// This does nothing, assuming the window hasn't changed its location.
popup.postMessage("The user is 'bob' and the password is 'secret'", "https://secure.example.net");

// This will successfully queue a message to be sent to the popup, assuming
// the window hasn't changed its location.
popup.postMessage("hello there!", "http://example.org");

Hàm windows.open() sẽ trả về reference của window mới được mở lên. Ở lần gọi popup.postMessage() đầu tiên, tin nhắn sẽ không được gọi đi, sẽ thất bại, vì targetOrigin mà ta gửi tới là “https://secure.example.net” trong khi origin thực tế của popup là “http://example.com:8080”. Trong lần gọi thứ hai thì thành công, nhưng bạn chưa thấy tác động của nó, để thấy chúng ta sẽ qua bước tiếp theo là code phần reciver.

Nhận message

Phương thức postMessage() sẽ phát sự kiện “message” trong đối tượng window của otherWindow. Vì vậy để lắng nghe message, thì bạn phải addEventListener cho event “message” của otherWindow trong script của otherWindow.

/*
 * In the popup's scripts, running on <http://example.org>:
 */

// Called sometime after postMessage is called
function receiveMessage(event)
{
    // Do we trust the sender of this message?
    if (event.origin !== "http://example.com:8080")
        return;
    console.log('message received:  ' + event.data,event);
}

window.addEventListener("message", receiveMessage, false);

Trước hết ta phải kiểm tra xem origin có đúng hay không, để tranh trường hợp người khác giả mạo sender cũng như xử lý những message không liên quan tới mình.  Message sẽ được chưa trong field “data” của tham số event.

Hồi đáp cho sender

Dây là liên hệ hai chiều, vì vậy bạn cũng có thể hồi đáp lại cho sender như chính cách mà bạn gửi message lúc nảy, tuy nhiên otherWindows bây giờ được chứa trong event.source.

event.source.postMessage('holla back youngin!',event.origin);

Bất cứ nơi nào muốn nhận event cũng đều phải handle sự kiện “message”. Nên bạn cần thêm đoạn code sau vào trang sender :

function receiveMessage(event)
{
    // Do we trust the sender of this message?  (might be
    // different from what we originally opened, for example).
    if (event.origin !== "http://example.org")
        return;

    // event.source is popup
    // event.data is "hi there yourself!  the secret response is: rheeeeet!"
}
window.addEventListener("message", receiveMessage, false);

Lời kết

Trước đó trong API của Chrome cũng hiện thực một API giống giống như thế này, nhưng sử dụng đối tượng đặt biệt chỉ được cung cấp trong môi trường chạy extension của Chrome mà thôi. Nó giúp giao tiếp giữa các extension với nhau, gửi message giữa backgroud script và content script, cũng có thể gửi message cho một ứng dụng native.

Nếu không có API này, mình đã rất khó để giải quyết vấn đề mà mình nói ban đầu, tại main page, mình phải sử dụng ajax để request liên tục xem user đã login được hay chưa, để update view kể cả khi họ chưa đóng popup, mà hồi đó mình cũng không hiểu tại sao, mà khi page bị reload hai ba lần, khi không còn handle event load của nó được nữa. API này khá thú vị đúng không.