Attaching functions to events in JavaScript: The unobstrusive way! - Part 2

Posted by Enrique Delgado Fri, 24 Oct 2008 13:00:00 GMT

Previously, I talked about a good way to attach event listeners to DOM elements. I like the approach described in that post because is pure JavaScript and not dependent on a framework, but if you happen to be using a framework already, or are thinking on using some, here are two methods of achieving this task:

It is important to remember that, your code should wait for the DOM tree to completely load before attempting to attach events to elements. The typical scenario is that some JavaScript code is attempting to attach an event to an element that does not exist yet, because the document is still loading.

Each framework has ways of wrapping your code, so that it runs only when the DOM is ready:

  • Prototype
    document.observe("dom:loaded", function() {
      // Your code here
    });
    
  • jQuery
    $(document).ready(function(){
       // Your code here
     });
    
    

Using Prototype

Prototype offers several event handling methods, but the most common is observe and stopObserving.

Example:

$('foo').observe('click', respondToClick);

function respondToClick(event) {
  // This is how you access the element that triggered the event:
  var element = event.element();
  // Your code here, e.g.:
  alert('Hello');
}

Notice the name of the event, in this case is click. The complete list of events are defined in the W3C recommendations, but here is a summary of the most common event names organized by type:

  • Mouse Events
    • click – Occurs when the pointing device button is clicked over an element.
    • mousedown – Occurs when the pointing device button is pressed over an element.
    • mouseup – Occurs when the pointing device button is released over an element.
    • mouseover – Occurs when the pointing device is moved onto an element.
    • mousemove – Occurs when the pointing device is moved while it is over an element.
    • mouseout – Occurs when the pointing device is moved away from an element.
  • Key Events
    • W3C does not have them, but the following are supplied by Prototype:
      KEY_BACKSPACE, KEY_TAB, KEY_RETURN, KEY_ESC, KEY_LEFT, KEY_UP, KEY_RIGHT, KEY_DOWN, KEY_DELETE, KEY_HOME, KEY_END, KEY_PAGEUP, KEY_PAGEDOWN
  • HTML Events
    • load – Occurs when the DOM implementation finishes loading.
    • unload – Occurs when the DOM implementation removes a document from a window or frame.
    • select – Occurs when a user selects some text in a text field.
    • change – Occurs when a control loses the input focus and its value has been modified since gaining focus.
    • submit – Occurs when a form is submitted.
    • focus – Occurs when an element receives focus either via a pointing device or by tabbing navigation.
    • blur – Occurs when an element loses focus either via the pointing device or by tabbing navigation.

Using jQuery

jQuery mostly has “helper methods” to set event listeners that are named after the event name, but the two most basic methods are bind and unbind.

Example:

$("#foo").bind("click", function(e){
  // This is how you access the element that triggered the event:
  var element = event.target;
  // Your code here, e.g.:
  alert('Hello');
});
Again, notice the name of the event, in this case is click. According to the jQuery documentation, these are the allowed event names:
  • blur, focus, load, resize, scroll, unload, click, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout, mouseenter, mouseleave, change, select, submit, keydown, keypress, keyup, error.

Most helper elements are named after the event names above, so you have things like click(), focus(), submit(), etc.

Example using an anonymous function as the functioned to be executed when the event is triggered:

$("#foo").click(function () { 
  // The element is access by using "$(this)" 
  // Your code here, e.g.:
  alert('Hello');
});

Event Delegation

One often source of confusion is how to attach event listeners to dynamically-generated DOM elements (for example, adding <li> elements to a list via AJAX).

When an element is crated, it is not already bound to an event listener, so you will have to perform a two-step process: create the element in the DOM, and then attach the event listener. This can get tedious easily, so instead of attaching the event listener to the DOM element desired, you attach an event listener to a parent element in the DOM tree.

In turn, this parent element acts as a “catch all” event listener. It will figure out which element triggered the event in the first place and act accordingly. This technique is referred to as event delegation.

Event delegation works because some events “bubble up” from child nodes to parent nodes in the DOM tree; see event bubbling.

Hope thins helps someone out there getting started. Post your questions if you run into trouble. :)

Posted in  | Tags , , , ,  | 2 comments

Slider Controls with Prototype and Scriptaculous

Posted by Enrique Delgado Tue, 11 Dec 2007 13:00:00 GMT

Even though web-based applications have the majority of input elements found in traditional desktop-based ones, once in a while you are going to wish you had input elements that for some reason did not make it to the HTML standards.

One of the input elements I wish were supported by default are draggable slider controls. There is something cool about having a slider to quickly change a setting of a search or a calculation, instead of having to type values on a text box or clicking on drop-down boxes.

Out of the myriad of options I found during a quick Google search, I found these two options to be the most attractive:

Since I was already using Scriptaculous on the application in question, I decided to stick to that solution. Yahoo’s version is definitely worth a look though. At first glance, Yahoo’s example looks better than the Scriptaculous one, but as you will see we can get the best of both worlds.

One word of warning; even though sliders are a useful control, you should exercise discretion as to not over use them. The truth of the matter is that not everyone is familiar using sliders on the web, and it can quickly become a usability problem if not careful. Good uses for sliders are things like mortgage calculators, search result filter controls, and things of that sort. Do not replace menus or tabs with a slider ;)

Onto the interesting part. In order to use Scriptculous’ sliders, you will need to make sure you are including the library with the slider component. By default, everything is included when you include scriptaculous in the traditional way:

<script src="prototype.js" type="text/javascript"></script>
<script src="scriptaculous.js" type="text/javascript"></script>

This will load about six components, but it is a good practice to load only those components that you are actually using. In my case, I’ll be using effects and slider so I’m calling Scriptaculous in this fashion:

 <script src="prototype.js" type="text/javascript"></script>
 <script src="scriptaculous.js?load=effects,slider" type="text/javascript"></script>
A Scriptaculous slider consist of two things:
  • A “parent” element (typically a DIV) deemed to be the “track” on which a knob will slide.
  • A “child” nested element (typically a DIV) that will act as the element to be slided across the length (or height) of the track.

You can create a slider track and controls purely based on CSS code like in the Scriptaculous sample, but I chose to use images within DIV elements to obtain the look of the Yahoo UI slider control. A possible HTML mark-up would be:

 <div id="track1" style="background-image:url(bg-fader.gif); background-repeat:no-repeat; width:209px; height:28px;">
  <div id="handle1" style="background-image:url(thumb-n.gif); background-repeat:no-repeat; width:17px; height:21px; cursor:move;"> </div>
 </div>

In the example above, we have a parent DIV element (the track) with an ID of track1 and a child (the handle) element with an ID of handle1. In this case the width and height of the DIV elements match the track and handle image dimensions.

Here is where the magic comes. Lets make a Slider object to give life to our HTML slider:

 <script type="text/javascript" language="javascript">
  // <![CDATA[
  window.onload = function() {
    new Control.Slider('handle1',
                       'track1',                                  
                       {axis:'horizontal',
                        range: $R(1,5)
                       });
  }
  // ]]>
 </script>

In the example above, we are creating a horizontal slider with a handle element ID of handle1 and a track element ID of track1. The slider possible values range from 1 to 5.

You may have noticed that the code is within the document’s onLoad event. This is because the slider object needs to be created after the track and slider elements have completely loaded. Alternatively, you could place the JavaScript code somewhere after the HTML elements in your document, but it is typically not a good idea to place JavaScript anywhere other than in its own .js file and then calling that file on your document. See my other post about unobstrusive JavaScript.

There are several options you can play with to create many variations of Scriptaculous sliders. For a good reference, check out the slider documentation page.

Download a complete working demo here. Just unzip the file and open the index.html file with your browser. You should see some sliders with different attributes and further examples as to how to get useful data from the sliders :)

Posted in  | Tags , , , , ,  | 10 comments