Search code examples
javascriptjqueryjquery-eventsstoppropagation

How to prevent the parent div from registering the click when a child class is clicked; event.stopPropagation(); doing funny things


We've got the classic problem with a div's child getting clicked and the parent's click event gets triggered as well. I've got a button set within a container that expands and unexpands upon clicking.

The button, when clicked, should:

  • Unexpand the container
  • Hide the container's description

The two click functions are given below:

var $NotificationContainer = $("#NotificationContainer");
$NotificationContainer.append('<div class="Notification" title="'+title+'"></div>');
var $thisNotification = $NotificationContainer.children('.Notification[title='+uniqueTitle+']');
$thisNotification.append('<div class="NotificationDescription">'+uniqueDescription+'</div>');
$(".NotificationDescription").hide();

// Button used to close an expanded notification
$thisNotification.append("<div class='NotificationCloseButton'></div>");
$('.NotificationCloseButton').hide();

$thisNotification.click(function()
{
        $(this).animate({height:250}, 1000);
        $(this).find('.NotificationDescription').slideToggle('fast');
        $(this).find('.NotificationCloseButton').slideToggle('fast');
});

$(".NotificationCloseButton").click(function()
{
        $thisNotification.animate({height:50}, 1000);
        $(this).find('.NotificationDescription').slideToggle('fast');
        $(this).find('.NotificationCloseButton').slideToggle('fast');
});

What I find with this code is that when clicking the close button:

  • SlideToggles the description to be hidden
  • SlideToggles the close button to be hidden
  • The container unexpands, but then re-expands (contents still hidden)

The $thisNotification click is being called (I think).


Now, when I try to use event.stopPropagation(); or a simple return false; in the closeButton's click, I get very interesting results.

Clicking the close button with either of the above additions now:

  • Unexpands the container
  • The description and button remain present, and do not slideToggle at all.

Code snippets of the exact way I implemented stopPropogation and return false:

$(".NotificationCloseButton").click(function(event)
{
    event.stopPropagation();
    $thisNotification.animate({height:50}, 1000);
    $(this).find('.NotificationDescription').slideToggle('fast');
    $(this).find('.NotificationCloseButton').slideToggle('fast');
});

and

$(".NotificationCloseButton").click(function()
{
    $thisNotification.animate({height:50}, 1000);
    $(this).find('.NotificationDescription').slideToggle('fast');
    $(this).find('.NotificationCloseButton').slideToggle('fast');
    return false;
});

Solution

  • You have click bindings for a the parent object:

    $thisNotification
    

    and for a child object:

    $(".NotificationCloseButton")
    

    When you click the close button, the 'click' event is being fired for both handlers, all animations are queued, and you get the undesirable closes-then-opens action.

    You have a few options to resolve this. #1 is to unbind the parent click handler and rebind it after the close button is clicked.

    $thisNotification.click(function()
    {
        notificationClickHandler(); //animations are separated from bindings
        $thisNotification.unbind('click');
    });
    

    Alternately, jQuery has a .clearQueue() method that removes all queued animations. This might have side-effects when users are quick with the mouse, or if your page is heavy on jQuery animations, so you'll have to experiment with the appropriate level of scope for your application.

    $(".NotificationCloseButton").click(function()
    {
        $thisNotification.animate({height:50}, 1000);
        $(this).find('.NotificationDescription').slideToggle('fast');
        $(this).find('.NotificationCloseButton').slideToggle('fast');
        $.clearQueue();
    });