If you click me again, I’m going to get touchy!

So, you can tell when the document is ready for manipulation and you can get nodes in the document. You can probably use normal JavaScript to do interesting things with those nodes. But you’re missing one thing that people need in order to use your app. You need interaction. In other words, you need event handling. You need to know when the user interacts with your app. If you’re using ChocolateChip to develop a mobile Web app, you need to get a finger on touch events. Clicks are for people who have mice, or hamsters, or gerbils, or whatever. People with mobile devices are smearing their greasy fingers all over those devices. You want to be able to capture those smears and do something with them.

ChocolateChip provides two very simple ways to deal with events: bind and unbind. Bind is not a cross-browser workaround that tries to play nice with the browser from that company in Redmond. Bind is using the browser’s standards-based event listener. Because of this, you can bind multiple events of the same type with different blocks of code to execute and they will all fire, unlike the inline event handers. But enough with the explanations. Let’s look at some examples. We’re going to start with some markup which has buttons for navigation, etc. One thing, because you can bind events to any element, I see no reason to use link tags to create buttons. I just hate having to do the return false thing to cancel the href attribute on a link. You can use a div or a span to make your button, and you don’t have to worry about canceling the href attribute or getting rid of the link’s default text underline on hover. The whole idea of ChocolateChip is how to get by with less. With that in mind, here’s my minimalistic markup for a navigation bar:

<article>
	<section>
		<header>
			<span class="button goHome">Back</span>
			<h1>Interface</h1>
			<span class="button about">About</span>
		</header>
		<nav>
			<ul class="list drilldown">
				<li rel="#RadioButtons">Radio Buttons</li>
				<li rel="#ProgressBar">Progress Bar</li>
				<li rel="#Tabs">Tabs with a Popup</li>
				<li rel="#Popup">Popup Window</li>
			</ul>
		</nav>
	</section>
</article>	

What we’re going to do with the buttons is just show how to bind touch events to handle touch interaction visually. In desktop browsers’ when the user’s mouse is over something with a hover state, the user is presented with a visual cue. Mobile devices have no mice, but your fat finger. Mobile devices do not have hover states, but using touch events we can indicate to the user that they’ve touched something by giving it a hover class. And when the touch ends, we remove the hover class. This gives the user the sense that he or she just interacted with your interface. Here’s the code to make that happen with the markup above:

$$(".button").each(function(button) {
    button.bind("touchstart", function() {
        this.addClass("hover");
    });
    button.bind("touchend", function() {
        this.removeClass("hover");
    });
})

Notice that we only need to get that collection of buttons once. Once we have it we can bind events for touchstart and touchend. Touchstart is like mousedown in a desktop browser, and touchend is like mouseup. When the user touches, we capture the event and invoke the ChocolateChip method addClass() to add a class of “hover” to the button. Similarly, when the user ends the touch, we remove the class hover using the ChococlateChip removeClass() method. In the above event handlers, “this” refers to the element being interacted with. Instead of adding and removing classes, you could write some other JavaScript code to do something to the objects referred to by “this.” The this keyword is the real Javascript keyword, not a framework specific reference wrapped in a framework object.

We could similarly add touch events to the list items in our markup. Here is the updated code:

$$(".button").each(function(button) {
    button.bind("touchstart", function() {
        this.addClass("hover");
    });
    button.bind("touchend", function() {
        this.removeClass("hover");
    });
});
$$(".list li").each(function(item) {
    item.bind("touchstart", function() {
        this.addClass("hover");
    });
    item.bind("touchend", function() {
        this.removeClass("hover");
    });
});	

We could also add some navigation to the list items by extending our touch event. We just need to get the rel attribute from each list item and direct the window location to it:

$$(".button").each(function(button) {
    button.bind("touchstart", function() {
        this.addClass("hover");
    });
    button.bind("touchend", function() {
        this.removeClass("hover");
    });
});
$$(".list li").each(function(item) {
    item.bind("touchstart", function() {
        this.addClass("hover");
        window.location = $(this.getAttribute("rel"));
    });
    item.bind("touchend", function() {
        this.removeClass("hover");
    });
});	

As you can see, with a few lines of code, ChocolateChip enables us to accomplish quite a lot. There’s no framework obfuscation going on. It’s human-readable as well as easy to maintain and extend.

Now if you’re a good developer, you know that when you’re done with doing things you need to remove your event handlers. You can do this with the unbind method. The unbind method is attached to the element upon which it fires, just like the bind event. Like the bind event, it accepts an event. It also needs the name of the function or method that the event was executing. Am I getting any raised eyebrows out there yet? OK, let me explain. If the function has no name, you can’t remove the event. What do all the above examples have in common? We’re binding events with anonymous functions. They’re called anonymous because they don’t have names. So, like I said, they can’t be unbound. If your app only has a few bound events, this probably won’t be a problem. If you have a lot of events, this will be a problem, especially if you adding and remove nodes with events attached to them. Not removing the events causes memory leaks, and these over time lead to the browser running out of memory, slowing down and eventually crashing.

To avoid this you have two choices, you can name your anonymous functions or you can do an event reset. One of the things developers love about anonymous functions is that you define them right where you’re executing them. Naming an anonymous function doesn’t mean you have to give up this freedom. Here’s the above code rewritten to use named functions instead of anonymous functions.

$$(".button").each(function(button) {
    button.bind("touchstart", function touched() {
        this.addClass("hover");
    });
    button.bind("touchend", function untouched() {
        this.removeClass("hover");
    });
});
$$(".list li").each(function(item) {
    item.bind("touchstart", touchedItem = function() {
        this.addClass("hover");
        window.location = $(this.getAttribute("rel"));
    });
    item.bind("touchend", untouchedItem = function() {
        this.removeClass("hover");
    });
});	

In the first each loop we bind the elements with named functions: touched() and untouched(). In the second each loop we set the anonymous functions to touchedItems and untouchedItems. Be aware that these functions now reside in the global space. If you wish to avoid polluting the global space with these functions, you can use a pattern of delegated events using event bubbling to capture the events at a higher level in the document. See the section on events for more details about this method of event capture.

Note:

One last thing, if you want to you can also add bindings for click events if you want to test your app in a desktop browser or if you want to make your Web app usable on modern desktop browsers as well.

If you are using ChocolateChip 1.0.7 or later you can also use event delegation as a way to track touch events.

Advertisements