Friday, April 17, 2015

Publish/Subscribe with jQuery

The frog dodgeball thing from Wednesday's comic was probably more fun for me to create than it was to play. Embedding a simple game in a comic, even a SVG/CSS/JavaScript comic, was not the easiest thing to do. But I used some very effective design patterns to make it easier.

Not a Sub, a Pub/Sub
The comic has supported simple animations since the beginning, but for the game to work I needed to add a custom module to just that one comic which could take additional actions when the animation state changed. I certainly didn't want to tightly couple the general-purpose animation code to this one comic, and also I didn't want to duplicate the most of the animation code in this module just to add one feature. Since I may want to do similar things in the future as well, I wanted a solution that would be flexible.

I decided to use the Publish/Subscribe Pattern. A derivative of the Observer Pattern, a Publish/Subscribe model allows me to completely decouple the event generator (my animation code in this case) from any other components that may wish to take action on certain events (the game code).

The Publish/Subscribe pattern has three parts:

  1. the publisher (creates the events)
  2. the subscriber(s) (want to be notified of events)
  3. the controller (takes events from publishers to subscribers).


My animated comic objects will publish events at certain points in their animation cycles. It doesn't matter if no one is subscribed to those events or if 100 other objects are subscribed. The general contract is that the subscriber says to the controller, "call my notify method when event X happens" and the publisher says to the controller, "event X just happened" the controller will pass the message on to every subscriber by calling the notify method. The publisher doesn't have any idea who is subscribed.

Fortunately, jQuery provides the controller. In jQuery-speak, this system is called custom events. Basically, the .trigger(event) method on jQuery objects invokes a controller that notifies any other objects that may be subscribed to that particular event on that object.

Consider the following example.

<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Pub/Sub</title>
</head>

<body>

  <p id="publisher">I have something to say.</p>

  <p id="target">Nothing here.</p>

</body>

<script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>

<script>

$(function() {

    // subscribe to events named "myEvent" from #publisher
    $("#publisher").on("myEvent", function(event, data) {

        $("#target").html(data.text);

    });

    // using setTimeout to simulate an asynchronous event
    setTimeout(function() {

        var myData = {
            text: "This is my event text"
        }
        // publish a "myEvent" event, with some custom data
        $("#publisher").trigger("myEvent", [myData]);

    }, 5000);

});

</script>

</html>

As soon as the page is loaded, it sets up a subscriber function to "myEvent" events that may come out of the P element named "publisher." I used setTimeout to simulate an asynchronous event that may happen sometime on your page. When it does, the "publisher" publishes its "myEvent" along with some data. The subscriber function is called, and the P element named "target" changes its contents to be whatever was provided as data.

If you execute this code yourself, you'll see that the text "Nothing here" is replaced by "This is my event text" after 5 seconds.

I used this pattern on IMG tags to have them publish events at certain points in their animation cycles, so I am now able to make comic-specific code that can take special actions on general-purpose animations. All I had to do was add a single line with a call to .trigger() to my animation function! That means comic-cell games will now be even easier for me to create! There will be more!

But not today. It's just a normal comic with a little animation.

Amphibian.com comic for 17 April 2015