Category Archives: jQuery

The Pub/Sub Pattern and Event Delegation in jQuery

18 Jan 2014

Recently I had a project that was a single-page micro site. Much of the content changed without reloading so it naturally made use of Ajax. There was DOM manipulation and jQuery was used throughout. I knew I would have a buttons and other elements triggering events. I always delegate events and am always looking for ways to improve upon how I’ve done things in the past. With that in mind and my recent foray into patterns that I don’t usually use I decided to use the PubSub pattern with the usual jQuery event registrations to come up with what I think is a clean method of hooking things up. I’d like to take a moment to talk about how I put it all together.

The Pub/Sub Pattern

The Pub/Sub pattern consists of a system where objects Subscribe to a task of some other object. Once subscribed the Publisher notifies each subscriber that the task they are subscribed to has occurred. In this case the subscription object is an array of objects whose properties are themselves arrays of objects populated with function references. As they are arrays its easy to add (“subscribe”) or remove an index of an array (“unsubscribe”) via the array push and slice methods.

When the pattern is applied we essentially look for the subscription object property that matches the event type. Once found we iterate through all of the function references assigned to it.

We of course have to come up with a method of filtering the events instead of blindly calling all of the subscribers to the given event type. My personal approach to validating an event is two-fold, 1) first validate the type of the event, then 2) look for an expected HTML data attribute and branch out from there. This 2-step approach tends to vary depending on what is happening in any given application but is easily adaptable.

There is more to the subject of Pub/Subs but that’s the gist of it. For a great read on patterns try this link.

Event Delegation with the Pub/Sub Pattern

Lay the foundation

So then lets talk about how to apply this in jQuery while delegating events. I’ll start with some basic JavaScript and create our namespace:

;(function(ns){
    // private
    var _subscriptions= {}; // will hold our subscribers

    // public
    ns.init = function(){
        console.log(typeof _subscriptions);
    }
})(this.app = this.app || {})

Our example is namespaced to “app”. To access its public methods you would use “app.methodName”, for example. Next we need a way to init the app and I’ll do so with jQuery’s document.ready.

;(function(ns){
    // private
    var _subscriptions= {};

    // public
    ns.init = function(){
        console.log(typeof _subscriptions);
    }
})(this.app = this.app || {})

$(document).ready(function(){
    app.init();
});

The above prints the word “object” to the console.

OK, lets whip up a quick HTML page – it will have a single div. It will also contain an unordered list.

<div id="box" style="width:100px;height:100px;background-color:#ccc;border:1px solid #000"></div>
<ul>
    <li>Red</li>
    <li>Blue</li>
    <li>Green</li>
    <li>Orange</li>
    <li>Yellow</li>
</ul>

We will be delegating click events on our LI tags, making them function as our “buttons”. I won’t bother styling them as these are quick and dirty examples, you can make them pretty if you like 😉

The Subscriber Function

Next we need to create a subscriber function to populate the _subscriptions object. The _subscriptions object will hold all the functions (function references, known as “subscriptions” in PubSub lingo) that have “subscribed” to a particular event.

;(function(ns){
    // private
    var _subscriptions= {};

    // public

    /* app.subscribe()
     * This function adds subscribers to the _subscribe object */
    ns.subscribe = function(eType,cb){
        if (!_subscriptions.hasOwnProperty(eType)){
            _subscriptions[eType] = [];
        }
        _subscriptions[eType].push(cb);
    }
    ...
})(this.app = this.app || {})
...

Lets look at the app.subscribe method; it requires 2 arguments:

  • eType – this is the type of the event, such as “click”, or “mouseover”. Yes, its a string.
  • cb – short for callback, this is the function that is waiting to be notified that the associated event took place.

As you can tell from looking at the code we are adding properties to _subscribers that correspond to the event names. If that property doesn’t exist (via hasOwnProperty) then we create it and assign it an empty array. The array will hold the functions (callbacks) that will be notified that the event occurred.

The Publisher Function

The publisher “publishes” events to the subscribers when they occur. I personally prefer to use the word “notify” instead of “publisher”, as in “when the event occurs the subscribers are notified”.

Here’s my app.notify function (aka the “Publisher”):

;(function(ns){
    ...
    /* app.notify()
     * This function notifies those subscribers who have subscribed to a specific event */
    ns.notify = function(e){
        e.stopPropagation(); // stop additional events from firing
        if (!$(e.target).attr('data-action')){ // should we do anything at all?
            e.preventDefault();
            return;
        }
        var cbs = _subscriptions[e.type]; // what type of event are we dealing with?
        for (var i=0;i<cbs.length;i++){ // notify all who have subscribed to the event
            cbs[i](e); // pass along the event object to the subscriber
        }
    }
    ...
})(this.app = this.app || {})
...

One important thing to note is that we need a way of validating that something should happen when an event occurs. As I mentioned previously this is a two-step process. The first thing that I personally do is look for the data-action attribute in the event target. “data-action” is my own personal convention, feel free to use whatever makes sense to you. Anyway, if it exists in the event target – regardless of its value – then I know that it is something that one of my subscribers might want to know about.

The second part of validating the event is within each of the subscribers themselves. I’ll describe this in the next section as it is related to our html.

Setting up your HTML

We need to add the data-action attribute to our LI tags so that we can have something to filter our events against else we won’t be able to qualify that the event is something to pay attention to.

<div id="box" style="width:100px;height:100px;background-color:#ccc;border:1px solid #000"></div>
<ul>
    <li data-action="changeDivColor">Red</li>
    <li data-action="changeDivColor">Blue</li>
    &l;li data-action="changeDivColor">Green</li>
    <li data-action="changeDivColor">Orange</li>
    <li data-action="changeDivColor">Yellow</li>
</ul>

Ok, great, however, if we are planning on changing the color of the div then we need to know the color to use. Lets add another attribute and call it data-color.

<div id="box" style="width:100px;height:100px;background-color:#ccc;border:1px solid #000"></div>
<ul>
    <li data-action="changeDivColor" data-color="cd2027">Red</li>
    <li data-action="changeDivColor" data-color="3252d9">Blue</li>
    <li data-action="changeDivColor" data-color="40b645">Green</li>
    <li data-action="changeDivColor" data-color="e79622">Orange</li>
    <li data-action="changeDivColor" data-color="e7e522">Yellow</li>
</ul>

Ok, we’re done here. By virtue of both data-action and data-color we know that a) we should pay attention to events originating from these elements and b) we know what color to use.

Also please note that data-action serves two purposes – yes, if it exists we’ll notify our subscribers, but also, each subscriber will pay attention to the value of data-action. If it isn’t what the subscriber is expecting then it simply returns and stops executing. This is important as you may have any number of subscribers to the same event but any one of them should only continue if the value of data-action jives with what the subscriber’s role in life is.

Speaking of which, lets look at what a subscriber might look like…..

Event Delegation – Creating Subscriptions and the Subscriber

Well, its about time we delegated an event…. lets delegate the click event by adding some jQuery to our app.init function:

;(function(ns){
    ...
    ns.init = function(){
        // delegate the click event
        $('body').on('click','li',app.notify);
    }
...
})(this.app = this.app || {})
...

We’re delegating the click event such that when it occurs it calls app.notify so that all functions subscribed to the “click” can be notified. Yes, its mapped to the list item element.

Of course we have yet to subscribe to anything so lets create a subscription, again, this happens inside of app.init:

;(function(ns){
    ...
    ns.init = function(){
        // create a subscription
        app.subscribe('click',app.changeColor);

        // delegate the click event
        $('body').on('click','li',app.notify);
    }
})(this.app = this.app || {})
...

We’re missing the changeColor function so lets create it. Note the “if” statement at the top of the following block of code where we’re checking to see if it’s value is what is expected. If not then we return and don’t proceed any further:

;(function(ns){
    ...
    ns.changeColor= function(e){
        if ($(e.target).attr('data-action') != 'changeDivColor'){
            return;
        }
        $('#box').css('background-color','#' + $(e.target).attr('data-color'));
    }
    ...
})(this.app = this.app || {})
...

Thats it – here’s the example followed by the complete javascript:

;(function(ns){
    // private
    var _subscriptions= {}; // holds our subscriptions

    // public

    /* app.subscribe()
     * This function adds subscribers to the _subscribe object */
    ns.subscribe = function(eType,cb){
        if (!_subscriptions.hasOwnProperty(eType)){
            _subscriptions[eType] = [];
        }
        _subscriptions[eType].push(cb);
    }

    /* app.notify()
     * This function notifies those subscribers who have subscribed to a specific event */
    ns.notify = function(e){
        e.stopPropagation(); // stop additional events from firing
        if (!$(e.target).attr('data-action')){ // should we do anything at all?
            e.preventDefault();
            return;
        }
        var cbs = _subscriptions[e.type]; // what type of event are we dealing with?
        for (var i=0;i<cbs.length;i++){ // notify all who have subscribed to the event
            cbs[i](e); // pass along the event object to the subscriber
        }
    }

    /* app.changeColor()
     * This function was subscribed to "click" events */
    ns.changeColor= function(e){
        if ($(e.target).attr('data-action') != 'changeDivColor'){
            return;
        }
        $('#box').css('background-color','#' + $(e.target).attr('data-color'));
    }

    /* app.init()
     * Init the app - setup event subscriptions and event delegation */
    ns.init = function(){
        // create a subscription
        app.subscribe('click',app.changeColor);

        // delegate the click event
        $('body').on('click','li',app.notify);
    }
})(this.app = this.app || {})

$(document).ready(function(){
    app.init();
});
Implications

With this system in place all you have to do to add another subscriber that should be notified of a click event is add the following line in app.init

app.subscribe('click',app.myNewFunction);

Or, if you want something to react to a different event, such as a change event you would need to do the above **and** associate the event with our notify function.

app.subscribe('change',app.myNewFunction);
$('body').on('change','input',app.notify)

and then subsequent subscriptions to the “change” event would just need to do a regular subscription call:

app.subscribe('change',app.myOtherFunction);

In larger apps I use the above as a sort of switchboard between my models, views and utility objects. I don’t have a chain of function calls where func 1 calls func2 which calls func3, all spread out over a large script or multiple files, etc. Instead, my subscribed functions receive their notifications of the desired event, they inspect the event target to see if it is what it they are expecting (via the data-action attribute) and then different function calls are made (i.e. func1, func2, func3). My subscribers all live in my controller object so there’s no function chain to chase down as I can easily go to my controller and see exactly what functions are called for specific events and “actions” (recall my “data-action” convention).

To illustrate, here’s the same example with a few more buttons and events. View the source to review the code:

Logging the _subscriptions object to the console shows that there are 5 subscribers to the “click” event and 1 subscriber to the “mousemove” event.

Object {click: Array[5], mousemove: Array[1]}
  click: Array[5]
    0: function (e){
    1: function (e){
    2: function (e){
    3: function (e){
    4: function (e){
    length: 5
    __proto__: Array[0]
  mousemove: Array[1]
    0: function (e){
    length: 1
    __proto__: Array[0]
    __proto__: Object

jqPlot Memory Leak

20 Nov 2013

Today I was brought into a conversation with our iOS developer about a portion of a client’s app that utilizes a charting component done in jQuery that is displayed via a web view. TestFlight showed the app crashing during prolonged use of the charting application. The first thing I thought was that memory wasn’t being released so I told the team I would look into it.

I ran the charting app in Chrome and watched the memory usage via the developer tool’s timeline. Sure enough, the familiar memory leak pattern became visible:

jqc_mem_leak_01

Using the tools Chrome provides to isolate a portion of the timeline and investigate what is happening revealed a theme of a “chart()” function being called. I tracked down where that was happening – and in the process discovered that jqPlot was in use (I had no previous experience with the app). I typed in a return statement at a stratgic spot to prevent charting from happening and then proceeded to once again torture the app and review the timeline. As you can see, the memory leak was gone:

jqc_mem_leak_02

No leak – the charting functions are the culprit – a quick search online revealed other people having the issue and one individual mentioning that jqChart has a “destroy” method that releases memory. Perfect, lets review our code again… and yes, all that is happening is that the previous developer is emptying the div wrapper of the chart data before filling it with a new one. That’s not releasing memory.

Here is the code before my edit:

$('#the_chart').html('');
var plot1 = $.jqplot ('the_chart', [fcccPoints, brandXPoints, crossoverPoint],
...

The div wrapper is unceremoniously emptied and the call to jqplot is assigned to a local variable. I needed to keep the reference to the chart so that I can destroy it properly so I moved the variable declaration outside of the function’s scope. I then changed the code to the following:

if (plot1){
    plot1.destroy();
    console.log('plot destroyed');
}
			
$('#the_chart').html('');
plot1 = $.jqplot ('the_chart', [fcccPoints, brandXPoints, crossoverPoint],
...

With those two tiny edits I again torture-tested the app and observed the timeline. As you can see jqPlot’s destroy method was doing its job and memory was now being reclaimed.

jqc_mem_leak_03

Mixing jQuery and reCaptcha in Forms

06 Oct 2013

The business of collecting user data includes the ubiquitous form. The thing with forms is that they are the first to see abuse on the Internet and if left unprotected your inbox or database will soon be overrun with spam. One of many methods of protecting forms is the CAPTCHA. CAPTCHAs require that a user interpret a randomly-generated image by typing its value into a text field. Since automated programs (bots) can’t read the image they are stopped cold. Humans and their famously short attention spans happen to have enough bandwidth to tolerate a CAPTCHA or two. The Spammer variants, on the other hand, seem to lack the patience needed and will quickly move on to easier pickings of which there are untold thousands. So then, while a CAPTCHA is a minor inconvenience to your end-users it’s presence more than makes up for itself by its ability to completely stave off an avalanche of useless form posts.

Google provides a CAPTCHA service called reCAPTCHA which is the subject of this article.

The workflow I’ll be implementing is the following:

  1. User “submits” form
  2. The form is validated
  3. If form validation succeeds I then make an AJAX call to the reCaptcha service via a proxy to see if the user entered the correct reCaptcha value
  4. If an incorrect value was entered I’ll display an in-line message to the user
  5. If the correct value was entered I will submit the form

You can see this process in action below:

Register for a set of reCAPTCHA keys

You must first sign up to use reCAPTCHA by visiting http://www.google.com/recaptcha. After you register click the “Add Site” link and add your site. After you add a site you will get 2 keys – keep these handy, we’ll need them later.

Download the reCAPTCHA PHP Library

You will also need to download the reCAPTCHA PHP Library (I’m assuming that your site is on a PHP-compatible server).

Setting up a proxy

Since we plan on making AJAX calls to the reCAPTCHA service we know we will need a proxy as we would otherwise run afoul of the browser’s cross-domain policy. The way we will circumvent this is by creating a proxy. This proxy will handle communication between our web page and reCAPTCHA by forwarding our submitted form data to the reCAPTCHA service which in return responds to the proxy and then the proxy hands the response back to our web page.

The reCAPTCHA PHP library contains everything we need to setup our proxy. The library contains 5 files – we will be using 2 of them:

  • recaptchalib.php
  • example-captcha.php

The second file – example-captcha.php – will need to be modified for our purposes. Here is the original file (this is version 1.11) before any changes:

<html>
  <body>
    <form action="" method="post">
<?php

require_once('recaptchalib.php');

// Get a key from https://www.google.com/recaptcha/admin/create
$publickey = "";
$privatekey = "";

# the response from reCAPTCHA
$resp = null;
# the error code from reCAPTCHA, if any
$error = null;

# was there a reCAPTCHA response?
if ($_POST["recaptcha_response_field"]) {
        $resp = recaptcha_check_answer ($privatekey,
           $_SERVER["REMOTE_ADDR"],
           $_POST["recaptcha_challenge_field"],
           $_POST["recaptcha_response_field"]);

        if ($resp->is_valid) {
            echo "You got it!";
        } else {
             # set the error code so that we can display it
             $error = $resp->error;
        }
}
echo recaptcha_get_html($publickey, $error);
?>
    <br/>
    <input type="submit" value="submit" />
    </form>
  </body>
</html>

To get to where we need to be we need to remove all html and make a couple of tiny changes to the PHP. I won’t walk through them in detail opting instead to just show you the finished edited file (the savvy among you will no doubt notice the differences) and to mention that the public and private reCAPTCHA keys are in use here:

<?php

require_once('recaptchalib.php');

// Get keys from https://www.google.com/recaptcha/admin/create
$publickey = "YOUR_PUBLIC_KEY_HERE";
$privatekey = "YOUR_PRIVATE_KEY_HERE";

# the response from reCAPTCHA
$resp = null;

# was there a reCAPTCHA response?
if ($_POST["recaptcha_response_field"]) {
    $resp = recaptcha_check_answer ($privatekey,
        $_SERVER["REMOTE_ADDR"],
        $_POST["recaptcha_challenge_field"],
        $_POST["recaptcha_response_field"]);

    if ($resp->is_valid) {
        echo "success";
    } else {
        echo "failure";
    }
}
?>

The line that echoes “success” is what we will be listening for in our AJAX call to determine if the reCAPTCHA verification was successful.

At this point your proxy is done. Go ahead and place your modified example-captcha.php and the unchanged recaptchalib.php on your server in the same folder as your form.

Add the jQuery and reCAPTCHA JS Libraries

You need to add these two lines within the head of your document (use whatever version of jQuery your project requires):

...
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js">
<script type="text/javascript" src="http://www.google.com/recaptcha/api/js/recaptcha_ajax.js">
...

Setting up your form

The form code below is pretty basic – things to pay attention to:

  • This is a template for the reCAPTCHA functionality only – you still need to configure your form to work with your form remailer (i.e., gdform.php on GoDaddy, formmail.php, cgiemail or whatever the case might be)
  • Note the onsubmit=”return doForm()” in the FORM tag – this is the hook to our JavaScript that communicates to the reCAPTCHA server
  • There are 2 divs present:

    • <div id=”recap”></div> this is the wrapper for the reCaptcha itself
    • <div id=”err”>…</div> This is the in-line error message that we will display if the reCAPTCHA verification fails.
       
  • Note the style tag – we have a css class called “hidden” that is assigned to the reCAPTCHA error div. If there is an error we use jQuery to remove the class and show the error message to the user
     
...
<style type="text/css">
    /* place the style in your stylesheet or move this to the head of your document */
    .hidden{
        display:none;
    }
</style>

<!-- note the onsubmit="return doForm()" - your FORM tag must contain this -->
<form action="test.php" method="post" name="form1" id="form1" onsubmit="return doForm()">

    <!-- your form fields would go in here -->
    Name: <input class="textInput" name="Name" type="text" id="name" size="55" maxlength="55" />

    <!-- The reCAPTCHA wrapper is here - this is required!! -->
    <div id="recap"></div>

    <!-- the reCAPTCHA error div is next, edit/style as you see fit -->
    <div id="err" class="hidden" style="background-color:#FFFF00;color:#FF0000;margin:12px 0px 12px 0px;">The Captcha was not entered correctly. Please try again.</div>

    <!-- the submit button -->
    <input type="submit" name="button" id="button" value="Submit" />
</form>
...

Inserting the reCAPTCHA, validating Form and reCAPTCHA input

Next we need to insert the reCAPTCHA elements into the document. In the code that follows you can see that I’m inserting the reCAPTCHA on jQuery’s document.ready event.

When the form is submitted I first validate the form input which you can see in the doForm() function. If validation passes I then attempt to validate the reCAPTCHA via the recapVerify function.

Its all put together in the code snippet below (** add your public key where indicated in line 41 **):

...
<script type="text/javascript">

    // VALIDATE THE FORM
    function doForm(){
	// your form validation would go here, for example:
	var isValid = $('#name').val().length != 0 ? true : false;

	if (!isValid){ // FORM VALIDATION FAILED
	    alert('Name is required');

	} else { //FORM VALIDATION SUCCEEDED, validate the reCAPTCHA
	    recapVerify(); // verify reCAPTCHA
	}
	return false;
    }

    // VALIDATE THE reCAPTCHA
    function recapVerify(){
        $.ajax({
	    type:'post',
	    url: 'captcha_proxy.php',
	    data: {
	        recaptcha_challenge_field:$('#recaptcha_challenge_field').val(),
	        recaptcha_response_field:$('#recaptcha_response_field').val()
	    }
	}).done(function(data, textStatus, jqXHR){
	    if (data == 'success'){
	        $('#err').addClass('hidden');
	        document.forms[0].submit();
	    } else {
	        $('#err').removeClass('hidden');
	    }
	}).fail(function(jqXHR,textStatus,errorThrown){
            console.log('proxy or service failure');
	});
    }

    // WHEN CALLED THIS INSETS THE reCAPTCHA INTO THE PAGE
    function reCapInsert(){
        Recaptcha.create('YOUR_PUBLIC_KEY_HERE',  // public key
        'recap',
            {
                theme: 'clean',
                callback: Recaptcha.focus_response_field
            }
	);
    }

    // WHEN THE DOM HAS LOADED FIRE THE reCapInsert FUNCTION TO INSERT THE reCAPTCHA
    $( document ).ready(function(){
        reCapInsert();
    });
	
</script>
...

And that should do it. Download all files here.

Preventing jQuery Mobile Page Flicker

11 Jun 2013

One irritating artifact of using jQuery Mobile is the page flickering that happens when changing pages – most noticeable on Android but sometimes on iOS as well. Here’s how to get rid of it…

First, make sure you have the proper viewport meta in your document:

...
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
...

This prevents the user from scaling the view as if it were a normal web page – every web app must have this in it.

Next, turn backfaces off via css like so:

...
.ui-page {
  -webkit-backface-visibility: hidden;
}
...

This last one assumes that you are not using native webview scrolling to scroll your content- I don’t know why you would want to – performance of native scrolling is spotty at best. So, assuming that you are not using native scrolling but are using iScroll instead then add this last item to your css:

...
body{
  overflow:hidden;
}
...

You’re done – no more flickering.

If you aren’t using iScroll but instead are relying on native webview scrolling then you will need to enable/disable the native scrolling programmatically during page transitions/loads. You can bind to the pagebeforechange event to do the toggling for your entire project. This event fires twice, once before and once after the page change, so you can do something similar to the following:

...
<style>
 .preventNativeScroll{
    overflow:hidden;
  }
</style>
...
$('.selector').on('pagebeforechange', function(e){
  var isScrollable = !$('body').hasClass('preventNativeScroll');
  if (isScrollable){
    $('body').removeClass('preventNativeScroll');
  } else {
    $('body').addClass('preventNativeScroll');
  }
});
...

I’ve not tested the above – its only meant for illustration.

jQuery onChange Event and Text Fields

18 Apr 2013

Seems like jQuery’s change event when bound to an input or textarea element doesn’t work like it should. What happens is that when those elements are blurred *then* onChange fires – completely not what we want to happen. According to the documentation this is actually by design. The only input elements that have an onChange event that behaves in a manner that makes sense are radios, checkboxs and select boxes. For anything else the onChange event will fire when the element loses focus!

...
$('input').change(function() {
  console.log('change, but only after the text input is blurred');
});

<input type=text">
...

There are a couple of events that you can use as an alternative, one is the “onInput” event. Curiously while this works in jQuery it is not part of jQuery’s API documentation (v1.9.1) – even though it works. The onInput event is part of the HTML 5 spec so my assumption is that jQuery’s support of it is here to stay, its just not part of the documentation for unknown reasons.

...
$('input').input(function() {
  console.log('behaves like a change event: as the text input element\'s value is changed the oninput event is fired');
});

<input type=text">
...

The caveat here – yes, you knew there would be one – is that since oninput is a member of the HTML5 spec that not every browser will support it. Thankfully IE9 and up supports it as does the crop of modern browsers.

For something more universal you might consider key bindings such as onKeydown or onKeyup, and then not worry about browser support.

...
$('input').keyup(function() {
  console.log('The text input element\'s value has changed');
});

<input type=text">
...

Logo

All content © 2012-2017.