Creating Custom Events with jQuery

By Rob Gravelle

 The Evolution of Event Handling

Once upon a time, developers added event handlers directly to an element in the HTML code using an "on…" attribute:

That was awful because you could only attach one handler to a given element at a time and added JavaScript code to the HTML source.

In time, browsers implemented the more modern Java-style event listener model whereby a function could listen for a particular event. In fact, Mozilla-based browsers emulated Java's implementation almost exactly, using the addEventListener() method to subscribe to messages, while Internet Explorer went with their own attachEvent() method.

Fast forward to present day and now you have the ability have your own event type fired from a DOM element or even from within a function. While not necessary, JS libraries like jQuery have made it easier than ever to utilize your own custom events in your scripts.

Piggybacking on Existing Events

Sometimes an element's event doesn't fully satisfy your requirements. For instance, it may tell you that a button was clicked, but lack some context or state that you want to track. In such a situation, your best bet is to append your own event to the existing one. In jQuery, the Event.trigger() method is the one that fires an event, as of version 1.7 (We'll be taking a look at the previous trigger() method shortly). It accepts an object that defines three properties - type, message, and time:

$.event.trigger({
  type:    "myTrigger",
  message: "myTrigger fired.",
  time:    new Date()
});

The new jQuery 1.7 Element.on() method can be used to bind the handler to an element's events. In the following example, an anonymous function is bound to a form's onsubmit event. Inside the function, an if statement tests for the form failing validation. Should that happen, it cancels the form.submit() event and triggers our "invalidFormData" event. Out event broadcasts the number of times that the form has failed validation using the attempts variable:

var attempts = 0;
$("#myForm").on("submit", function (evt) {
  if (!validator.validate()) {
    //cancel form submission
    evt.preventDefault();
    $.event.trigger({
      type: "invalidFormData",
      message: ++attempts + " invalid form submission attempts.",
      time: new Date()
    });
  }
});

Functions that want to subscribe to the "invalidFormData" event may do so using the Document.on() method:

$(document).on("invalidFormData", function (evt) {
  $('txtForSubmissionAttempts').val( "Event fired from "
                                   + evt.currentTarget.nodeName + " at "
                                   + evt.time.toLocaleString() + ": " + evt.message
  );
});

Firing Events from Functions

jQuery's trigger() method does not necessarily have to be associated with a DOM node. In fact, it can be called from anywhere in your code. The key is to use the Document.trigger() method. In contrast to the jQuery 1.7 trigger() method, this one accepts an event name and an optional data argument:

strategyFund.transferIn = function(amt, transactionFeeContributions, fromAccount) {
  fromAccount.withdraw(amt + transactionFeeContributions['sideFund']);
  this.deposit(amt - transactionFeeContributions['strategyFund']);
  //fire our custom event
  jQuery(document).trigger('broker_fees_incurred_buy', transactionFee);
  
  return amt;
};

On the listener side, we use the Document.bind() method to associate our handler with the event. In the following example, both the broker_fees_incurred_buy and broker_fees_incurred_sell events are bound to the same handler function:

function updateBrokerFees(e, brokerFee) {
 console.log(e.type + ' - updating broker fees: ' + totalBrokerFees);
 totalBrokerFees += brokerFee;
}
jQuery(document).bind('broker_fees_incurred_buy',  updateBrokerFees);
jQuery(document).bind('broker_fees_incurred_sell', updateBrokerFees);

Outside Events

If that isn't enough for you, Ben Alman has also developed a jQuery plugin for Outside Events, which allow you to bind to an event that will be triggered only when an event occurs outside the element in question. A good usage for the plugin would be to have a modal dialog respond to a mouse click anywhere outside the dialog to close it, in addition to clicking the standard "close" button. The plugin would replace a background overlay that would register outside mouse clicks.

The plugin supports over ten default "outside" events such as click outside, double-click outside, mouse-over outside, and focus outside. The best part is that you can easily define your own outside events to supplement the default ones using the jQuery.addOutsideEvent() method:

jQuery.addOutsideEvent( event_name [, outside_event_name ] );

The event_name is the name of the originating event that the new "outside" event will be powered by. This event can be a native or custom event, as long as it bubbles up the DOM tree. The optional outside_event_name is the name for the new "outside" event. If omitted, the outside event will be assigned the event_name plus the "outside" suffix, i.e., "dragoutside".

Here's some code that causes an "incrementoutside" event to fire when when ever the "increment" event is fired from any element other than the $('elem') one:

// add our event
jQuery.addOutsideEvent( 'increment' );

var outsideTracker = 0;
// bind our handler to the new event
$('elem').bind( 'incrementoutside', function(event){
  var elem   = $(this),
      target = $(event.target);
  outsideTracker++;
}); 

// fire/dispatch the event
$('anotherElem').trigger( 'increment' );

Conclusion

Custom events are a great way to execute loosely-related peripheral code when the exact ordering doesn't really matter. I have found that the best suited tasks involve tracking counts, statistics, calculations, logging, and whatever else may be tied to a given process but not intrinsic to it.



Make a Comment

Loading Comments...

  • Web Development Newsletter Signup

    Invalid email
    You have successfuly registered to our newsletter.
  •  
  •  
  •