At Second Street, we are proud to make white-label software that enables our customers to offer promotions and contests as part of their brand. One of the interesting development challenges behind that has been the need to embed our software on our customers' websites.

An <iframe> can solve quite a few problems on the web, but it introduces a nearly-impenetrable wall of security. The only way through that wall, if your iframe is on a different domain, is via the HTML5 postMessage API. Unfortunately, postMessage is messy.

The Problem with postMessage

While it's great to have something to break the security barrier between a frame and the page it's embedded on, postMessage has its downsides.

  • Your event listener gets every message, from every frame. The signal-to-noise ratio is abysmal.
  • There is no guarantee that the other side is ready to receive your message yet.
  • Only strings can be sent over the wire.
  • The concept of a request/response cycle is completely absent.

Our Tiny Solution

We created a library, talker.js, to solve this problem. At under 1kB minified and gzipped, it's perfect for embedding into applications to make communicating over postMessage a breeze. And since it's released under the open source MIT License, you can start using it today!

Talker.js wraps postMessage in an easy-to-use API. It handles queueing your messages until it completes a handshake so it knows both sides are ready, it filters out messages that aren't from Talker on the other side, and it is built on JavaScript Promises, so you can send a request and receive a response.

Using Talker.js

The Talker constructor takes a window, and an origin (or '*' to accept messages from any origin).

var talker = new Talker(myFrame.contentWindow, 'http://example.com/');  

Sending Messages

Use Talker#send to send a message to the other side. Messages have a namespace for organization, and can have an object sent for data transfer. The object must be able to pass through JSON.stringify.

talker.send('myNamespace', { data: 'here' });  

Listening for Messages

Talker will call Talker#onMessage with a Talker.IncomingMessage. That message has properties for the namespace and data it was originally sent with, as well as an id and a reference to its talker.

talker.onMessage = function(message) {  
  console.log(message.namespace, message.data);
  console.log(message.id, message.talker);
};

Responding to Messages

Use Talker.IncomingMessage#respond to respond to a message with an object. This returns a promise via PinkySwear.js that may resolve with a response if one is sent, or may reject with an error.

talker.onMessage = function(message) {  
  message.respond({ hello: 'there' });
};

talker.send('localStorage', { get: 'username' })  
  .then(function(message) {
    console.log(message.namespace, message.data);
    console.log(message.id, message.talker);
  }, function(error) {
    console.error(error);
  })
;

Getting Talker

Talker.js distributions are available via Bower and GitHub. The source is also on GitHub. Talker is available as a global, a named or anonymous AMD package, or a Common JS package.

$ bower install talkerjs --save

If you'd like to contribute, please Fork us on GitHub, or file an issue with any bug reports or feature requests.