ChocolateChip provides a number of methods for dealing with events. They’re listed blow:

Element.bind()
Binds a event and callback to an element.
Element.unbind()
Removes and event and its callback from an element.
Element.removeEvents()
Sets all events on an element to null.

Element.bind()

This method binds the event to the element it is called on. It takes two arguments: the event enclosed in parentheses and a function to execute when the event occurs.

$.ready(function() {
    $("#home").bind("touchstart", function() {
        window.location = "http://css3wizardry.com"; 
    });
    $$("li").forEach("touchstart", function(item) {
        $(this.getAttribute("rel")).addClass("current");
    }
    $$(".button").forEach("touchstart", function(button) {
        button.addClass("touched");
    });
    $$(".button").forEach("touchend", function(button) {
        button.removeClass("touched");
    });
});

Element.unbind()

ChocolateChip provides an unbind method for removing any events from an element. This is especially important to do before removing/deleting any elements from your document in order to prevent memory leaks. There also may be the rare case where you only want the use to be able to perform an action once. In such a case, you can execute your code and as the last step unbind the event.

To unbind and event you need to pass in two arguments: the event that was bound and the name of the function that it executed. That means that if you were binding events with anonymous function, you will be unable to unbind them. There are several ways to work around this. This first is to declare all your functions separately with proper names. Or you could simply provide a name for the anonymous function while you are passing it to the event. You can do this in two ways:

$.ready(function() {
    $$(".button").forEach("touchstart", function touchedButton(button) {
        button.addClass("touched");
    });
    $$(".button").forEach("touchend", untouchedButton = function(button) {
        button.removeClass("touched");
    });
});

In the above example we have provided our events with function defined as arguments, like anonymous function, but these are both named. In the first example it is a normal named function. In the second example it is an anonymous function assigned to the global variable “untouchedButton.” Actually, in both cases these functions now reside in the global space. They are not private. Note that in the second example you cannot use the keyword “var” to assign the anonymous function to a variable. This would result in a parse error. With these functions now named we could remove them when necessary.

$.ready(function() {
function unbindEvents() {
    $$(".button").forEach(function(button) {
        button.unbind("touchstart", touchedButton);
        button.unbind("touchend"), untouchedButton);
    });
}	
window.onunload = unbindEvents;
});

Element.removeEvents()

If you really like anonymous functions, but you want a way to negate your events at some point, there is another alternative. ChocolateChip provides a utility function that parses all events attached to an element and sets them to null. It’s easy to execute. Just call it on the element.

$.ready(function() {
    $$(.button).forEach(function(button) {
        button.removeEvents();
    });
});

Event Delegation with Event Bubbling

If you have a collection of elements that you want to bind events to, instead of binding the event to each element, it would be more efficient to use event delegation to capture the events at a higher level. For example, if you have a list and you want to attach events to each list item you could do as follows:

$.ready(function() {
	$$("ul#menu li").forEach(function(item) {
            item.bind("touchstart", function() {
                item.addClass("touched");
            });
        });
});

This is fine to do if you have a few list items, but if your list has many items and you have many such lists throughout your application, this is very inefficient. It eats up memory and slows down your app. On top of that, if the list items are dynamic, you would have to write some event listeners to rebind events to any new items added to the list. And easier way is to attach an event handler to the list itself and listen for events on the list items. This way each list has only one bound event and it doesn’t matter how many list items there are or if any are removed or added. The event on the list will always capture any interaction with its child elements. In order to know what child element was the receiver of the event, we use the DOM’s target property. We can check whether it is a list item or not and then define what we want to do with it. Here’s an example:

$.ready(function() {
    $$("ul#menu").bind("touchstart", function(e) {
         if (e.target.nodeName.toLowerCase() === "li") {
               e.target.addClass("touched");
         }
    });
});

So, in the above code, we check to see if the target is a list item. If it is, we add a class to it. This simple pattern can be used whenever you have a collection of elements that you need to attach events to. Remember, attach an event to the parent of the collection and check the target to see if it is the element type you are interested in. If it is, do what you need to with it. Because we’re checking for list items, when the e.target is equal to a list item, we use e.target as the reference to the list item we wish to attach the class to.

The following code has both methods: attaching events to every list item and listening for events on the list itself:

$.ready(function() {
    // In this example events are attached to each list item:
    $$("ul#menu li").forEach(function(item) {
        item.bind("touchstart", function() {
            console.log(item.innerText);
        });
    });
    // In this example an event listener is attached to the list:
    $("ul#menu").bind("touchstart", function(e) {
         if (e.target.nodeName. === "LI") {
             console.log(e.target.innerText);
         }
    });
});

Both examples have the same result, touching a list item will output its content to the console. However, in the second version the code is more efficient because there is only one bound event. Remember that when using this technique of event delegation, you want to bind the event listener as close to the collection of elements as possible. This is because the evens bubble up from the target to where you’ve bound the event listener. In other words, binding all you event listeners to the body element would be extremely inefficient because all events would have to bubble all the way up to the body tag before you could capture them. So, attach you event listeners where it makes sense.

Update Nov 19, 2010

As of version 1.0.7, ChocolateChip has two new methods for delegating events and triggering events:

Element.delegate()

The Element.delegate() method takes three arguments: selector, event, callback. It attaches an event listener on the element to track the supplied event on its descendant nodes described by selector. When the designated event occurs, it executes the callback on the target element. The method passes the target element to the callback as an argument so that you have its scope inside the callback. Here’s an example of how to use the delegate method. Notice that we can reference the item that was touched by using the term item (could use any term here, it’s just a reference to the target element):

$("ul").delegate("li", "touchstart", function(item) {
    item.css("background-color: yellow");
});
$("ul").delegate("li", "touchend", function(item) {
    item.css("background-color: white");
});

Note: Jan 16, 2011

As of ChocolateChip version 1.0.9 the delegate method now works with elements having child nodes. This means that if you want to listen for an event on a complex control that has many descendant elements, the event will bubble up to the control to capture it. In previous versions the child elements would be the event target preventing delegation from working except on simple constructions. So, say you were delegating touch events for a list and the list items had text and images. With the earlier version of Element.delegate, if the user where to touch the images or text, the list item would not be the target of the event and the delegation would fail. Now this has been resolved. Now you can delegate events without worrying about whether the targets are simple or complex elements.

Element.trigger()

The element.trigger() method allows you to trigger an event on another element when interacting with another element, for example clicking on one element triggers a click on another element. To accomplish this, you execute the trigger method on the target element with the event you wish to trigger. Here’s how:

$("#link").bind("click", function() {
    console.log("You just clicked this link!");
});
// By clicking on #importantButton, a click will be triggered on #link button:
$("#importantButton").bind("click", function() {
    // Trigger an event on another element:
    $("#link").trigger("click");
});

New in ChocolateChip 2.0

Element.on()

This is equivalent in function to the same method in jQuery and Zepto. It expects at least an event type and a callback.

$(function() {
   $('#clickButton').on('click', function() {
      console.log('You just clicked this button!');
      console.log(this.nodeName) // returns name of node.
   };
});

You can also use the same method to implement a delegate event listener. The following example implements a delegate on the body tag to listen for a click on any element with a class of ‘button’:

$(function() {
   $('body').on('click', '.button', function() {
      console.log('You just clicked this button!');
      console.log(this.nodeName) // returns name of node.
   };
});

Element.off()

This is the equivalent of unbind() and undelegate().

$(function() {
	// Normal event unbinding:
   $('#clickButton').off('click', 'buttonClickHandler');
   // Delegate unbinding:
   $('body').on('click', '.button', 'buttonClickHandler');
});
Advertisements