One of the main goals of architecting an API is to supply a single function for each task and follow the Once and Only Once principle. However, a common problem with this approach is that it leads to a massive library of functions for achieving relatively simple tasks. As a digital marketing agency, the dev team at Zion & Zion is often called upon to build custom, extensible javascript functionality.

Of course, there are ways around forcing the end-user to have to call several functions to get a single piece of information, such as function composition, but this comes with its own cons. With more composition, the end-user loses control over the in-between steps of a functional composition. jQuery triggers can fix that.

The Problem

As an example, let’s consider an API that handles interacting with a slideshow. You might have a method for shifting to the next slide:

Slideshow.prototype.nextImage = function() {
    var activeClass = 'active';

    $(this.slides.active).removeClass(activeClass);
    this.slides.active = this.getNextSlide();
    $(this.slides.active).addClass(activeClass);
};

Fairly straight forward; remove ‘active’ from the current slide, store the next slide as the current slide, and add the ‘active’ class to the new current slide. The above method is responsible for one thing: shifting to the next image. But let’s say the end-user wants to run their own code between removing the ‘active’ class from the first slide and getting the next slide. The flexibility simply isn’t there. They could overload the method entirely and write in their own code, but why not give them the flexibility while keeping the DRY principle in mind. This is where jQuery triggers come into play.

The Solution

jQuery triggers are really just event emitters, beefed up to allow extra functionality while working flawlessly across browsers. Let’s jump right in and see what adding a couple of triggers looks like:

Slideshow.prototype.nextImage = function() {
    var activeClass = 'active';

    $(this.slides.active).removeClass(activeClass);
    $(this).trigger('removeActive.slideshow');
    this.slides.active = self.getNextSlide();
    $(this.slides.active).addClass(activeClass);
    $(this).trigger('addActive.slideshow');
};

Not a lot changed; we added two triggers to the slideshow. The user now has the ability to listen for these triggered events and run any code they want:

$('.slideshow').on('removeActive.slideshow', function(e) {
	$( this ).find('previous').removeClass('previous');
	$( this.slides.active ).addClass('previous');
});

$('.slideshow').on('addActive.slideshow', function(e) {
	$( this ).find('previous').removeClass('next');
	$( this ).next().addClass('next');
});

Although this is an overly simplistic example, you can see the newfound flexibility we’ve given the developer. They can now run any code they want in between the functions we’ve written into our method.

Take note of the ‘.slideshow’ in our trigger name. This is a namespace and allows the developer to target only our events, regardless if there are other libraries which trigger the same event name. Imagine if we had a ‘resize’ trigger. If the developer were to hook into the ‘resize’ event, they would also be hooking into the browser resize event. To avoid the conflict, we trigger ‘resize.slideshow’ instead.

Passing Custom Data

We can also pass any arbitrary data we’d like into the trigger, and it will be available to the developer when they hook into it. Taking the previous example, let’s pass in the active slide so it’s easier to access on their end:

Slideshow.prototype.nextImage = function() {
    var activeClass = 'active';

    $(this.slides.active).removeClass(activeClass);
    $(this).trigger('removeActive.slideshow', [this.slides.active]);
    this.slides.active = this.getNextSlide();
    $(this.slides.active).addClass(activeClass);
    $(this).trigger('addActive.slideshow', [this.slides.active]);
};

Notice the second argument added to our triggers; this is an array of parameters to push into the callback the developer creates. In our case, we’re only passing the active slide. Now the developer can do the following:

Slideshow.prototype.nextImage = function() {
    var activeClass = 'active';

    $(this.slides.active).removeClass(activeClass);
    $(this).trigger({
        type: 'removeActive.slideshow',
        active: this.slides.active
    });
    this.slides.active = this.getNextSlide();
    $(this.slides.active).addClass(activeClass);
    $(this).trigger({
        type: 'addActive.slideshow',
        active: this.slides.active
    });
};

Now the developer can use the event object to gain access to any arbitrary data you set. In this case, the active element:

$('.slideshow').on('removeActive.slideshow', function(e) {
	$( this ).find('previous').removeClass('previous');
	$( e.active ).addClass('previous');
});

$('.slideshow').on('addActive.slideshow', function(e) {
	$( this ).find('previous').removeClass('next');
	$( e.active ).next().addClass('next');
});

Here, the ‘type’ property will be used as the event name, and all other properties are attached to the event object passed as the first parameter to the callback the developer writes. Pretty nifty.

Conclusion

Triggers can greatly increase the flexibility of your code and give developers using your library a chance to hook into any place you add them. They’re incredibly performant and add very little to the size of your code. In general, by adding only two triggers to each method in any library, you can give a developer the ability to customize your libraries a great deal to suit most needs they have.