Hostingheaderbarlogoj
Join InMotion Hosting for $3.49/mo & get a year on Tuts+ FREE (worth $180). Start today.
Advertisement

jQuery Succinctly: Events & jQuery

by
Gift

Want a free year on Tuts+ (worth $180)? Start an InMotion Hosting plan for $3.49/mo.

This post is part of a series called jQuery Succinctly.
jQuery Succinctly: HTML Forms & jQuery
jQuery Succinctly: jQuery and the Web Browser

Not Limited To a Single ready() Event

It is important to keep in mind that you can declare as many custom ready() events as you would like. You are not limited to attaching a single .ready() event to the document. The ready() events are executed in the order that they are included.

Notes: Passing the jQuery function, a function - e.g. jQuery(funciton(){//code here}) - is a shortcut for jQuery(document).ready().


Attaching/Removing Events Using bind() and unbind()

Using the bind() method - e.g. jQuery('a').bind('click',function(){}) - you can add any of the following standard handlers to the appropriate DOM elements.

Obviously, based on DOM standards, only certain handlers coincide with particular elements.

In addition to this list of standard handlers, you can also leverage bind() to attach jQuery custom handlers - e.g. mouseenter and mouseleave - as well as any custom handlers you may create.

To remove standard handlers or custom handlers, we simply pass the unbind() method the handler name or custom handler name that needs to be removed - e.g. jQuery('a').unbind('click'). If no parameters are passed to unbind(), it will remove all handlers attached to an element.

These concepts just discussed are expressed in the code example below.

<!DOCTYPE html>
<html lang="en">
<body>
    <input type="text" value="click me">
    <br>
    <br>
    <button>remove events</button>
    <div id="log" name="log"></div>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
      // Bind events
      $('input').bind('click', function () { alert('You clicked me!'); });
      $('input').bind('focus', function () {
          // alert and focus events are a recipe for an endless list of dialogs
          // we will log instead
          $('#log').html('You focused this input!');
      });
      // Unbind events
      $('button').click(function () {
          // Using shortcut binding via click()
          $('input').unbind('click');
          $('input').unbind('focus');
          // Or, unbind all events     // $('button').unbind();
      });
  })(jQuery); </script>
</body>
</html>

Notes: jQuery provides several shortcuts to the bind() method for use with all standard DOM events, which excludes custom jQuery events like mouseenter and mouseleave. Using these shortcuts simply involves substituting the event's name as the method name - e.g. .click(), mouseout(), focus().

You can attach unlimited handlers to a single DOM element using jQuery.

jQuery provides the one() event handling method to conveniently bind an event to DOM elements that will be executed once and then removed. The one() method is just a wrapper for bind() and unbind().


Programmatically Invoke a Specific Handler Via Short Event Methods

The shortcut syntax - e.g. .click(), mouseout(), and focus() - for binding an event handler to a DOM element can also be used to invoke handlers programmatically. To do this, simply use the shortcut event method without passing it a function. In theory, this means that we can bind a handler to a DOM element and then immediately invoke that handler. Below, I demonstrate this via the click() event.

<!DOCTYPE html>
<html lang="en">
<body>
    <a>Say Hi</a>
    <!-- clicking this element will alert "hi" -->
    <a>Say Hi</a>
    <!-- clicking this element will alert "hi" -->
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
      // Bind a click handler to all <a> and immediately invoke their handlers
      $('a').click(function () { alert('hi') }).click();
      // Page will alert twice. On page load, a click
      // is triggered for each <a> in the wrapper set.
  })(jQuery); </script>
</body>
</html>

Notes: It is also possible to use the event trigger() method to invoke specific handlers - e.g. jQuery('a').click(function(){ alert('hi') }).trigger('click'). This will also work with namespaced and custom events.


jQuery Normalizes the Event Object

jQuery normalizes the event object according to W3C standards. This means that when the event object is passed to a function handler you do not have to worry about browser-specific implementations of the event object (e.g. Internet Explorer's window.event). You can use the following attributes and methods of the event object worry-free from browser differences because jQuery normalizes the event object.

Event Object Attributes

  • event.type
  • event.target
  • event.data
  • event.relatedTarget
  • event.currentTarget
  • event.pageX
  • event.pageY
  • event.result
  • event.timeStamp

Event Object Methods

  • event.preventDefault()
  • event.isDefaultPrevented()
  • event.stopPropagation()
  • event.isPropagationStopped()
  • event.stopImmediatePropagation()
  • event.isImmediatePropagationStopped()

To access the normalized jQuery event object, simply pass the anonymous function, passed to a jQuery event method, a parameter named "event" (or whatever you want to call it). Then, inside of the anonymous callback function, use the parameter to access the event object. Below is a coded example of this concept.

<!DOCTYPE html>
<html lang="en">
<body>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
      $(window).load(function (event) { alert(event.type); }); // Alerts "load"
  })(jQuery); </script>
</body>
</html>

Grokking Event Namespacing

Often we will have an object in the DOM that needs to have several functions tied to a single event handler. For example, let's take the resize handler. Using jQuery, we can add as many functions to the window.resize handler as we like. But what happens when we need to remove only one of these functions but not all of them? If we use $(window).unbind('resize'), all functions attached to the window.resize handler will be removed. By namespacing a handler (e.g. resize.unique), we can assign a unique hook to a specific function for removal.

<!DOCTYPE html>
<html lang="en">
<body>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
      $(window).bind('resize', function ()
      { alert('I have no namespace'); });

      $(window).bind('resize.unique', function () { alert('I have a unique namespace'); });

      // Removes only the resize.unique function from event handler
      $(window).unbind('resize.unique')
  })(jQuery); </script>
</body>
</html>

In the above code, we add two functions to the resize handler. The second (document order) resize event added uses event namespacing and then immediately removes this event using unbind(). I did this to make the point that the first function attached is not removed. Namespacing events gives us the ability to label and remove unique functions assigned to the same handler on a single DOM element.

In addition to unbinding a specific function associated with a single DOM element and handler, we can also use event namespacing to exclusively invoke (using trigger()) a specific handler and function attached to a DOM element. In the code below, two click events are being added to <a>, and then using namespacing, only one is invoked.

<!DOCTYPE html>
<html lang="en">
<body>
    <a>click</a>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
      $('a').bind('click',
       function () { alert('You clicked me') });
      $('a').bind('click.unique',
          function () { alert('You Trigger click.unique') });  // Invoke the function passed to click.unique
      $('a').trigger('click.unique');
  })(jQuery); </script>
</body>
</html>

Notes: There is no limit to the depth or number of namespaces used - e.g. resize.layout.headerFooterContent.

Namespacing is a great way of protecting, invoking, removing any exclusive handlers that a plugin may require.

Namespacing works with custom events as well as standard events - e.g. click.unique or myclick.unique.


Grokking Event Delegation

Event delegation relies on event propagation (a.k.a. bubbling). When you click an <a> inside of a <li>, which is inside of a <ul>, the click event bubbles up the DOM from the <a> to the <li> to the <ul> and so on, until each ancestor element with a function assigned to an event handler fires.

This means if we attach a click event to a <ul> and then click an <a> that is encapsulated inside of the <ul>, eventually the click handler attached to the <ul>, because of bubbling, will be invoked. When it is invoked, we can use the event object (event.target) to identify which element in the DOM actually caused the event bubbling to begin. Again, this will give us a reference to the element that started the bubbling.

By doing this, we can seemly add an event handler to a great deal of DOM elements using only a single event handler/declaration. This is extremely useful; for example, a table with 500 rows where each row requires a click event can take advantage of event delegation. Examine the code below for clarification.

<!DOCTYPE html>
<html lang="en">
<body>
    <ul>
        <li><a href="#">remove</a></li>
        <li><a href="#">remove</a></li>
        <li><a href="#">remove</a></li>
        <li><a href="#">remove</a></li>
        <li><a href="#">remove</a></li>
        <li><a href="#">remove</a></li>
    </ul>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
      $('ul').click(function (event) { // Attach click handler to <ul> and pass event object
          // event.target is the <a>
          $(event.target).parent().remove(); // Remove <li> using parent()
          return false; // Cancel default browser behavior, stop propagation
      });
  })(jQuery); </script>
</body>
</html>

Now, if you were to literally click on one of the actual bullets of the list and not the link itself, guess what? You'll end up removing the <ul>. Why? Because all clicks bubble. So when you click on the bullet, the event.target is the <li>, not the <a>. Since this is the case, the parent() method will grab the <ul> and remove it. We could update our code so that we only remove an <li> when it is being clicked from an <a> by passing the parent() method an element expression.

$(event.target).parent('li').remove();

The important point here is that you have to manage carefully what is being clicked when the clickable area contains multiple encapsulated elements due to the fact that you never know exactly where the user may click. Because of this, you have to check to make sure the click occurred from the element you expected it to.


Applying Event Handlers to DOM Elements Regardless of DOM Updates Using live()

Using the handy live() event method, you can bind handlers to DOM elements currently in a Web page and those that have yet to be added. The live() method uses event delegation to make sure that newly added/created DOM elements will always respond to event handlers regardless of DOM manipulations or dynamic changes to the DOM. Using live() is essentially a shortcut for manually having to set up event delegation. For example, using live() we could create a button that creates another button indefinitely.

<!DOCTYPE html>
<html lang="en">
<body>
    <button>Add another button</button>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
      $('button').live('click', function ()
      { $(this).after("<button>Add another button</button>"); });
  })(jQuery); </script>
</body>
</html>

After examining the code, it should be obvious that we are using live() to apply event delegation to a parent element (<body> element in the code example) so that any button element added to the DOM always responds to the click handler.

To remove the live event, we simply use the die() method-e.g. $('button').die().

The concept to take away is the live() method could be used to attach events to DOM elements that are removed and added using AJAX. In this way, you would forgo having to rebind events to new elements introduced into the DOM after the initial page load.

Notes: live() supports the following handlers: click, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout, keydown, keypress, keyup.

live() only works against a selector.

live() by default will stop propagation by using return false within the function sent to the live() method.


Adding a Function to Several Event Handlers

It is possible to pass the event bind() method several event handlers. This makes it possible to attach the same function, written once, to many handlers. In the code example below, we attach a single anonymous callback function to the click, keypress, and resize event handlers on the document.

<!DOCTYPE html>
<html lang="en">
<body>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
      // Responds to multiple events
      $(document).bind('click keypress resize', function (event) { alert('A click, keypress, or resize event occurred on the document.'); });
  })(jQuery); </script>
</body>
</html>

Cancel Default Browser Behavior With preventDefault()

When a link is clicked or a form is submitted, the browser will invoke its default functionality associated with these events. For example, click an <a> link and the Web browser will attempt to load the value of the <a> href attribute in the current browser window. To stop the browser from performing this type of functionality, you can use the preventDefault() method of the jQuery normalized event object.

<!DOCTYPE html>
<html lang="en">
<body>
    <a href="http://www.jquery.com">jQuery</a>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
      // Stops browser from navigating
      $('a').click(function (event) { event.preventDefault(); });
  })(jQuery); </script>
</body>
</html>

Cancel Event Propagation With stopPropagation()

Events propagate (a.k.a. bubble) up the DOM. When an event handler is fired for any given element, the invoked event handler is also invoked for all ancestor elements. This default behavior facilitates solutions like event delegation. To prohibit this default bubbling, you can use the jQuery normalized event method stopPropagation().

<!DOCTYPE html>
<html lang="en">
<body>
    <div><span>stop</span></div>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
      $('div').click(function (event) {
          // Attach click handler to <div>
          alert('You clicked the outer div');
      });

      $('span').click(function (event) {
          // Attach click handler to <span>
          alert('You clicked a span inside of a div element');
          // Stop click on <span> from propagating to <div>
          // If you comment out the line below,
          //the click event attached to the div will also be invoked
          event.stopPropagation();
      });
  })(jQuery); </script>
</body>
</html>

In the code example above, the event handler attached to the <div> element will not be triggered.


Cancelling Default Behavior and Event Propagation Via return false

Returning false - e.g. return false - is the equivalent of using both preventDefault() and stopPropagation().

<!DOCTYPE html>
<html lang="en">
<body><span><a href="javascript:alert('You clicked me!')" class="link">click me</a></span>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function($){     $('span').click(function(){
      // Add click event to <span>
      window.location='http://www.jquery.com';     });
      $('a').click(function(){
          // Ignore clicks on <a>
          return false;
      });
  })(jQuery); </script>
</body>
</html>

If you were to comment out the return false statement in the code above, alert() would get invoked because by default the browser will execute the value of the href. Also, the page would navigate to  jQuery.com due to event bubbling.


Create Custom Events and Trigger Them Via trigger()

With jQuery, you have the ability to manufacture your own custom events using the bind() method. This is done by providing the bind() method with a unique name for a custom event.

Now, because these events are custom and not known to the browser, the only way to invoke custom events is to programmatically trigger them using the jQuery trigger() method. Examine the code below for an example of a custom event that is invoked using trigger().

<!DOCTYPE html>
<html lang="en">
<body>
    <div>jQuery</div>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
$('div').bind('myCustomEvent', function () {
      // Bind a custom event to <div>
      window.location = 'http://www.jquery.com';
  });
      $('div').click(function () {
          // Click the <div> to invoke the custom event
          $(this).trigger('myCustomEvent');
      })
  })(jQuery); </script>
</body>
</html>

Cloning Events As Well As DOM Elements

By default, cloning DOM structures using the clone() method does not additionally clone the events attached to the DOM elements being cloned. In order to clone the elements and the events attached to the elements you must pass the clone() method a Boolean value of true.

<!DOCTYPE html>
<html lang="en">
<body>
    <button>Add another button</button>
    <a href="#" class="clone">Add another link</a>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
$('button').click(function () {
var $this = $(this);
      $this.clone(true).insertAfter(this);
      // Clone element and its events
      $this.text('button').unbind('click'); // Change text, remove event
  });
      $('.clone').click(function () {
          var $this = $(this);
          $this.clone().insertAfter(this); // Clone element, but not its events
          $this.text('link').unbind('click'); // Change text, remove event
      });
  })(jQuery); </script>
</body>
</html>

Getting X and Y Coordinates of the Mouse in the Viewport

By attaching a mousemove event to the entire page (document), you can retrieve the X and Y coordinates of the mouse pointer as it moves around inside in the viewport over the canvas. This is done by retrieving the pageY and pageX properties of the jQuery normalized event object.

<!DOCTYPE html>
<html lang="en">
<body>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
$(document).mousemove(function (e) {
      // e.pageX - gives you the X position
      // e.pageY - gives you the Y position
      $('body').html('e.pageX = ' + e.pageX + ', e.pageY = ' + e.pageY);
  });
  })(jQuery); </script>
</body>
</html>

Getting X and Y Coordinates of the Mouse Relative to Another Element

It is often necessary to get the X and Y coordinates of the mouse pointer relative to an element other than the viewport or entire document. This is usually done with ToolTips, where the ToolTip is shown relative to the location that the mouse is hovering. This can easily be accomplished by subtracting the offset of the relative element from the viewport's X and Y mouse coordinates.

<!DOCTYPE html>
<html lang="en">
<body>
    <!-- Move mouse over div to get position relative to the div -->
    <div style="margin: 200px; height: 100px; width: 100px; background: #ccc; padding: 20px">
        relative to this </div>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function($){  $('div').mousemove(function(e){
      //relative to this div element instead of document
      var relativeX = e.pageX - this.offsetLeft;
      var relativeY = e.pageY - this.offsetTop;
      $(this).html('releativeX = ' + relativeX + ', releativeY = ' + relativeY);
  });
  })(jQuery); </script>
</body>
</html>
Advertisement