PixelMEDIA Inc.

Skip to content

Blog

 Jeff L

Binding a click event inside a click event with jQuery

Posted by Jeff L
September 29th, 2009

I was recently working on coding a design that included a simple type of faux select element. It was basically an unordered list that expanded and collapsed when you clicked a link. As it was styled similar to a select element, the JavaScript behaviors were similar as well. Clicking the link expanded the list underneath it, and you could then choose a link inside the list and navigate away from the page, or click the original link to close the list again.

The client, however, found this a bit confusing—once they had expanded the hidden list, it wasn’t obvious to them how to close it. They wanted to be able to click somewhere else on the page and have the list close so they could continue browsing. No problem, I figured, a few JavaScript tweaks and we’d be all set. Simply putting a click event on the body element to close the list would be pretty straightforward.

Lets take a look at the code we used:

function initFauxSelect() {
  $('a.showSelect').click( function(e) {
      e.preventDefault();
      var $theList = $(this).parent('.fauxSelect').next('.selectList');
      hideShow($theList);
  });
}
 
function hideShow(obj) {
  $theList = obj;
  if ($theList.is(':visible') ) {
    $theList.hide();
  } else {
    $theList.show();
  }
}

It’s a simple click event on a.showSelect that calls the hideShow() function. If the list is visible, hide it, otherwise show it.

I figured it would be as simple as binding a new click event to the body tag whenever we show the list. The new event would hide the list when someone clicked elsewhere on the page. Let’s look at the updated hideShow() code:

function hideShow(obj) {
  $theList = obj;
  if ($theList.is(':visible') ) {
    $theList.hide();
    $('body').unbind('click.fauxselect');
  } else {
    $theList.show();
    $('body').bind('click.fauxselect', function(e) {
      $theList.hide();
      $('body').unbind('click.fauxselect');
    });
  }
}

Now, whenever we’re showing the list, we’re creating a new click event on the body that hides the list. It also unbinds itself once its job is done. If someone closes the list by clicking a.showSelect again, we also unbind the event just to keep things clean.

However, I ran into a problem. The event I was binding to the body was being called right away, which stopped the list from ever appearing! It seemed a bit strange, as my assumption was that you’d never want your new event to fire as the same time as the current event.

Thankfully, there’s an easy way prevent this from happening. In our original click event on the .showSelect element, we need to stop that event from bubbling up the DOM.

function initShortcutsSelect() {
  $('a.showSelect').click( function(e) {
    e.preventDefault();
    e.stopPropagation();  // ----- ADDED THIS LINE
    var $theList = $(this).parent('.fauxSelect').next('.selectList');
    hideShow($theList);
  });
}

By telling the event to stopPropagation, it never bubbles up the DOM, never reaches the body and never calls the new click event you bound there until the user actually clicks the body.

Tags: , ,

2 Responses to “Binding a click event inside a click event with jQuery”

  1. Patrick O'Neill Says:

    Yeah event propagation can be obnoxious, but it does come in handy when you’re using event delegation techniques.

    I had a”fake select” to do recently that worked similarly – except in mine I have it where it auto-collapses if the user has moved out of the select area for a given amount of time (500ms, I think).

    I think a more elegant/abstracted solution might be keep that custom click event bound to the body at all times (as I recall, binding/unbinding is fairly intensive, though I could be wrong) and keep the original hideShow() – performing an “is(‘:visible’)” check each time.

    And of course, I’d keep all the functions in a namespaced object … just in case. ;-)

  2. Jeff L Says:

    Patrick,

    Appreciate the feedback! So you’d simply bind a click event to the body that would call hideShow() every time? I wanted to clean up the event each time as I was concerned that every click on the page would call that function. I suppose some proper testing is in order. :-)

    And yes, of course in the real code all this functionality is wrapped up in namespaced objects. I broke it out just for the blog post.

Leave a Reply:

» One or more required feilds missing