Walter Rumsby's Web Site

Handling Events in JavaScript

2010-10-24

This post is adapted from an answer on an internal Q & A site at work. It's not "new" information, but it might be new to some people so I thought that it was worth reposting here. The code examples use YUI 2 because that's what I'm most familiar with, but other JavaScript libraries provide similar features.

Let's start from "the old way" and progress to "the new way".

If we start with:

<a href="#" onclick="alert('I like frogs!')">Frogs?</a>

this will, not surprisingly, alert the text "I like frogs", but there's a simple problem here: the onclick handler does not cancel the default behaviour associated with clicking on the link, meaning that clicking on the link will navigate to the destination anchor. You might not notice this if the link is at or near the top of the page, but you've had to scroll vertically to reach the link then clicking the link will scroll the page back to the top. Preventing this is simple:

<a href="#" onclick="alert('I like frogs!'); return false">Frogs?</a>

return false cancels the default behaviour of the event.

This is all very well for simple cases, but if you want to perform a number of steps writing all of that in the onclick attribute can become unweildy. Again, there's a relatively simple solution, call a function from your onclick handler:

<script>
    var frogs = function() {
        alert('I like frogs');
        // do some other stuff too
    };
</script>
<a href="#" onclick="frogs(); return false">Frogs?</a>

This is also nice because the function can be defined in a external script file, which:

  • can be cached
  • should reduce the size of the HTML sent to the browser

On the other hand the onclick attribute can only reference global objects and functions (without some trickery, which would make your onclick attribute unweildy) and global variables are bad.

Luckily we can set the onclick attribute in script, e.g.:

<a id="link" href="#">Frogs?</a>
<script>
    window.onload = function() {
        // frogs is not visible outside of this function
        var frogs = function() {
            alert('I like frogs');
            // do some other stuff too
        };

        document.getElementById('link').onclick = function() {
            frogs();
            return false;
        };
    };
</script>

or:

<a id="link" href="#">Frogs?</a>
<script>
    document.getElementById('link').onclick = function() {
        alert('I like frogs');
        // do some other stuff too
        return false;
    };
</script>

Awesome, but we have another problem: there can only be a single value for an attribute, which means that setting onclick replaces any previous value of onclick. This problem is more obvious when when trying to work with events like the window object's onload event.

Fortunately the W3C defines the DOM Events Specification for attaching event listeners, meaning that you can attach multiple listeners for a single event on a given element.

<a id="link" href="#">Frogs?</a>
<script>
    document.getElementById('link').addEventListener('click', function(e) {
        alert('I like frogs');
        // do some other stuff too
        e.preventDefault(); // instead of return false
    }, false);
</script>

Unfortunately Internet Explorer does not implement the DOM Events Specification and offers a different syntax for adding event listeners. The scope in which the event handler runs in IE is also different. Quirks Mode has some more information on the differences between browsers.

Fortunately, JavaScript libraries come to the rescue, providing a normalised API that works across browsers and a host of other features.

<script>
    YAHOO.util.Event.onDOMReady(function() {
        var Event = YAHOO.util.Event;

        Event.on('link', 'click', function(e) {
            alert('I like frogs');
            // do some other stuff too
            Event.preventDefault(e);
        });

        Event.on('link', 'click', function(e) {
            alert('ME TOO!');
            Event.preventDefault(e);
        });
    });
</script>
<a id="link" href="#">Frogs?</a>

Things to note:

  • the order of execution of event handlers is not guaranteed meaning that "ME TOO!" might be alerted before "I like frogs"
  • these examples are simply to demonstrate the concepts, you can do much more powerful and interesting things with a good event library; have a look at the documentation, including the examples

YUI 2 provides a really robust Event Utility, YUI 3 provides an even more sophisticated system. This is really important because the only really good definition of how a web application differs from a web site that I have come across states:

When it boils down to it, the main differentiator of a web application and a web site is that an app has much more interaction and is process-focused rather than content-driven.

Custom Events mean that you're not limited to DOM Events either.

To me this all sounds quite a bit like enterprise integration: you have a powerful system for dealing with loosely-coupled components and that can form the basis for an interesting application framework.

Working with events is a fundamental part of writing contemporary web applications. JavaScript libraries provide very capable utilities to deal with events. Take advantage of those utilities and avoid the problems associated with other approaches.

Comments

IE 8 and the User Agent String

2009-08-12

Earlier this week I had the opportunity to get a better understanding of IE 8's Browser Mode and Document Mode when trying to diagnose an issue that only a coworker could reproduce. I'd marked an issue as resolved, while he was still experiencing the problem.

There was a bit of back and forward where we compared version numbers, made sure caches were cleared, inspected response headers, etc. until we realised the key difference was that he was running the default Intranet Zone settings, where "Display intranet sites in Compatibility View" is enabled, while I was not.

I then noticed that when running with Browser Mode: Internet Explorer 8 Compatibility View the User-Agent request header identifies itself as MSIE 7.0. When running with Browser Mode: Internet Explorer 8 the User-Agent identifies itself as MSIE 8.0. This is case even if the Document Mode is Internet Explorer 8 Standards (e.g. even if you have set the X-UA-Compatible response header or meta element to IE=8).

From IE8 and the X-UA-Compatible situation:

It is entirely possible to encounter IE8 sending the IE7 User Agent string to the server while using the IE8 Standards Mode rendering engine; conversely, you can encounter the IE8 User Agent string while rendering in IE7's engine.

This has one major consequence: you cannot rely on the user agent string when dealing with IE 8. The Browser Mode is entirely different to the rendering mode (aka the Document Mode).

Luckily IE 8 provides a new property, document.documentMode, which you can use to work out which rendering mode is being used. If you need to do something specific for IE 8 Standards Mode you can work out if that rendering mode is being used by checking if (document.documentMode == 8).

As part of this work I developed a greater understanding of what the various Browser Modes mean:

  • Internet Explorer 7 - act as IE 7 (meaning you can't use Internet Explorer 8 Standards Document Mode even if X-UA-Compatible: IE=8; report User Agent version MSIE 7.0)
  • Internet Explorer 8 - act as IE 8 (if X-UA-Compatible is set follow this flowchart to choose the Document Mode; otherwise rely on the DOCTYPE to chose between Quirks Mode and IE 8 Standards Mode; report User Agent version MSIE 8.0) default for the Internet Zone
  • Internet Explorer 8 Compatibility View - act as IE 8 in Compatibility View (choose the Document Mode as for Browser Mode: IE8, choose between Quirks Mode and IE 7 Stanards Mode if there is no X-UA-Compatible header/meta; report User Agent version MSIE 7.0) default for the Intranet Zone

Initially it's quite confusing, but if you sit with it for a while it makes sense - for a user visiting your site the things that will influence the Browser Mode and Document Mode are:

  1. Compatibility View Settings
  2. Intranet Zone vs. Internet Zone
  3. DOCTYPE
  4. X-UA-Compatible
  5. Have they changed the Browser Mode or Document Mode using the Developer Toolbar

o_O

Comments

Tom Coates: Designing For A Web Of Data

2008-09-21

You can never have too much sweet, sweet data

Photo by wasabicube

At Webstock 2006 Flickr was the darling of the conference. It seemed to be mentioned by almost every speaker so I was curious to see which site(s) would be fêted in 2008. Flickr still got more mentions than any other site (Twitter came in at number 2 by my reckoning), but this time round there was more of a focus on the Flickr platform and not simply on Flickr's design innovations (AJAX, tagging, vowel reduction, etc.).

Tom Coates covered this most effectively in what I personally saw as the best/most important/key speech of Webstock 2008. For me the theme of the whole conference shifted from the 06's focus on web design & usability to the idea of the web as a platform and Coates addressed this topic directly.

What follows is more or less a rough transcription of the presentation.

Introduction

Flickr, Twitter, Google, Upcoming, Facebook, delicious, last.fm, Dopplr and Amazon are all special sites and services. They are more than just web sites. They've managed to break out of the frame of the browser and manifest themselves onto desktops, devices and other sites. They provide a platform on which other things can be built by anyone and as a result those sites have benefited - they've generated more revenue, more ethusiasm and had a bigger impact than they would have had if they did not provide those platform services.

Five Things

  1. Your Web Site Is Not Your Product
  2. You Must Play Well With Others
  3. You Can Never Have Too Much Data
  4. Hierarchies Can't Take Weight
  5. Collaboration Is Important

What Is The Web Of Data?

The web of "pages" is changing to the web of "data". Web pages are now simply one way of accessing the data.

In this context "data" means "stuff that a computer can access" (e.g. blog posts, pictures, videos, book reviews? resources).

So what we have is

A web of data sources [and] services for exploring and manipulating data and ways people can plug them together.

Your Web Site Is Not Your Product

Your territory is anywhere your network touches. For example people can interact with Twitter using the web site, IM clients, SMS, anything that can broadcast information to Twitter. 90% of the traffic to Twitter is via their API (i.e. only 10% via the web site). This means that the Twitter web site is not the product - the product is a way for people to keep in touch via short messages and the site is merely one instantiation of that.

Similarly there are other ways to interact with Flickr: eStarling, Moo cards, widgets, web site badges, desktop clients, mashups, etc.

In the case of last.fm many users interact with last.fm through a desktop player app, and not through the web site at all. OS X users can also play customised radio streams in iTunes and not have to spend any time looking at the last.fm web site.

This is all stuff you can do today! As the network touches more "things" computing becomes ubiquitous and while you could say that many of the applications today are niche, toy, "play" apps there are other possibilities for a wide range of applications.

When the web of data bleeds into the real world:

  • Physical objects respond to and visualise data from a network (e.g. Nabaztag; Ambient Orb)
  • Interactions with physical objects allows people to change data stored on the network
  • Physical objects act as a sensor that writes to the web of data (e.g. Wattson)

You Must Play Well With Others

If a web site is just one manifestation of your data you need to design for recombination because there are opportunities when your data can be mashed up with other datasources:

  • You can drive people to your site (e.g. Amazon)
  • People might pay for your data
  • You put yourself (your data) in an ecosystem that contains similar data
  • It makes your service more attractive to users
  • It reduces amount of central development (Flickr couldn't build everything that people wanted from the product so they let others build on top of Flickr)

Other examples: Oakland Crimespotting combines two datasets which can be understood in the context of each other (crime by geography, geography by crime); Fire Eagle (provides a geolocation service that other services can use to provide another layer of data on top of an existing service).

You Can Never Have Too Much Data

2,264,930,075

Photo by wasabicube

2,264,930,075. That's the number of photos on Flickr as of February 2008. That's 1 photo for every 3 people on the planet and that number increased by more than 900,00 in the 12 hours before Coates' talk.

How do you make that scale of data communicable, explorable, navagable to users? By finding more data!

  • Capture meta data during the production of the object (e.g. data about when a photo was taken; the mode used; etc)
  • Analyse the object
  • Crowd-source the creation of meta data (e.g. tagging)
  • Perform behavioral analysis (e.g. Google - PageRank is based on how other pages link to a site)

For this photo of Piccadilly Circus, there are a number of related data axes:

It's even possible to explore photos by apeture if you know what you're doing. The only reason this feature isn't more obvious is that the Flickr team haven't worked out a good user interaction to do this via the web site.

Hierarchies Can't Take Weight

Information Architecture circa 1999 was about organising your data into hierarchies (taxonomies). IA circa 2005 was about organising your data into folksonomies (e.g. tagging).

You can have both! Amazon.com provides multiple ways to explore data, using both traditional taxonomies and folksonomies.

Hierarchies don't scale. Peter Morville who presented earlier also made this point and it's not something that is unique to the web. "Weblike" navigation is a much better way of exploring large data sets - "top navigation is just the jumping off point".

Collaboration Is Important

Coates works at Yahoo! Brickhouse which is set up to run like a startup inside a large organisation. The teams at Brickhouse are small, inter-disciplinary teams (e.g. BravoNation, Fire Eagle).

A key idea here is that idea generation is not the responsibility of one role (e.g. programmers, graphic designers), but is shared across roles. Coates strongly believes this is the best (only?) way to create new products.

Conclusion

Not a lot to disagree with in what was covered. It's interesting to me how web-based applications have evolved and have to deal with tremendous scale and complexity. Back in 1999 I can remember a friend bemoaning the fact he was working on web sites and not "real" applications. As we grow a richer appreciation of how we can develop applications on the web the kinds of solutions being developed are far more interesting than the client/server applications of the 90s.

If Google Maps was the catalyst for CEOs/CIOs demanding AJAX-based solutions it stands to reason that once they understand the concept that you can create a web-based platform their view of what's possible will broaden in a similar way (then again maybe there was an element of "oh this graphic/web designery stuff is easy, we could do that too!"; it seems likely that if "normal companies" were to develop platforms that they'd turn to Google, Amazon, etc. to provide [some of the] infrastucture).

And finally, while I agree that "weblike navigation" works better for large datasets, it's not a big deal if you don't stumble across a particular photo of your favourite MC when using Flickr, but I can't help thinking that for certain classes of applications there is a large set of information that users have to see. Hierarchies obviously don't work in those situations, but I'm not sure folksonomies completely solve that problem either.

Lots of stuff to think about. Since it took me 7 months to write this post I can now add that Coates' is returning for Webstock 09!

Comments

On DOM Ready

2008-06-25

YUI provides the YAHOO.util.Event.onDOMReady method which "lets you define a function that will execute as soon as the DOM is in a usable state."

I've heard reports (both internally and on the YUI mailing list) about this method going a bit haywire - specifically firing before the DOM is usable - in Internet Explorer, but these seem to be in weird situations and it has been (seemingly) impossible to create a test case to demonstrate when/where/why this problem happens. This makes it pretty difficult to raise a bug that the YUI team can look at, so the problem still (apparently) exists.

I was looking at an old post by Dean Edwards that suggests using the script element's defer attribute to work out when the DOM is "usable" in IE. Edward's solution suggests using conditional comments to remove the defer attribute for non-IE browsers, but I wonder if that's even necessary.

This leads me to think, assuming your external script is entirely wrapped up in an onDOMReady function, why not throw a defer attribute onto the script element? My hunch is:

  • IE will defer the script's execution until the DOM is usable.
  • Other browsers will ignore the defer attribute, but since onDOMReady works for those browsers (assuming they are A Grade browsers) they will also wait until the DOM is usable.
  • By the time IE trys to work out if it should fire onDOMReady the defer attribute will mean that it's okay for it to fire, so you should sidestep the "onDOMReady fires in IE before the DOM is usable" issue.
  • All of this will happen before the window's load event fires regardless of browser.

I've set up an example page which appears to confirm this, but of course this page doesn't suffer from the incredibly tricky to replicate problem so this is very much a guess at a solution. The other thing is this assumes that all the code in your script runs inside an onDOMReady function, which might not be what you want.

I've followed this up with a message on the YUI mailing list to see if anyone can spot any problems with this approach and plan to update this post with anything that comes out of that.

Update: there is now a bug with test cases. Hopefully this is resolved in the next YUI release, but in the interim, or if you are using an older YUI release, it appears that adding the defer attribute to script elements does avoid these errors.

Comments