Question

-- UPDATE --

I was a little bit stupid, the child UL's were covering the parent list (opacity fadeout), so I couldn't click the groups after i clicked them once. Oops! Anyhow thanks for helping me out guys!


The problem with jQuery propagation is something everyone is wrestling with. But here is my case which I can find nowhere to fix (or maybe I am dumb to not look at the right places or my jQuery skills just aren't good enough to understand).

The base case is that I have some groups, and in the groups there are several options. when you click on one of the groups, the options will pop up. BUT, when I click one of the options the whole popping up activity fires again, because it are the children of the parent, and so he recognizes the class where the children are catched in.

My HTML looks as follows:

<div id="buttons">
        <ul>
            <li class="group">Group 1
                <ul>
                    <li class="link">Link</li>
                </ul>
            </li>
            <li class="group">Group 2
                <ul>
                    <li class="link">Link</li>
                </ul>
            </li>
            <li class="group">Group 3
                <ul>
                    <li class="link">Link</li>
                </ul>
            </li>
            <li class="group">Group 4
                <ul>
                    <li class="link">Link</li>
                </ul>
            </li>
        </ul>
</div>

I guess that looks quite simple and nothing is wrong with that I suppose. It are just lists nested within lists.

But then my jQuery code. It looks like this:

$(".group").click(function(event) {

    // if ($(event.target).is('ul li ul li')) return false;

    $(".group").children("ul").animate({top: 0, opacity: 0}, 200);
    var liHeight = ($(this).children("ul").children("li").height())+20;
    var totalOptions = $(this).children("ul").children("li").length;
    var combinedHeight = totalOptions * liHeight;
    var slideIn = -(16+combinedHeight) // formula doing math on slidedistance
    $(this).children("ul").animate({
                            top: slideIn,
                            opacity: 1
                        }, 600);

    /*
    $(".link").click(function(event) {
        event.stopPropagation();
    });
    */

});

What it actually does is:

  1. resetting the group children to default top position
  2. getting the clicked group
  3. getting height of the total child ul of the target group
  4. preparing the distance for the animation
  5. firing the animation

There is no problem coming along with that and all works as I wants, but when I click on the lis within the uls, it just keeps firing the animation, and that is not part of the plan.

I commented out 2 blocks of code as you might have noticed. That are 2 different ways for controlling propagation I found while stumbling the internet for a relative problem with answer. Both give no good result though. What happens is that the propagation actually stops, but makes it also unable to click different groups.

That is the problem in a nutshell. I think I just need to have one or two lines to add, but I just can't think of what should fit, almost tried everything that came to my mind.

Also, if you can't find any answers but have some feedback on the code markup or something, it's also welcome (I'm a student in interaction design / web development, so I would appreciate that too :))

Was it helpful?

Solution

You should not install the click handler on the .group item, but only on the toggle element.

<li class="group">
    <a class="toggle">Group 2</a>
    <ul>
        <!-- contents to show & hide -->
    </ul>
</li>

$(".toggle").click(function(e) {
    $(this).next()... // to reference the ul
})

OTHER TIPS

Just move the following code outside of the .group click handler:

$(".link").click(function(event) {
    event.stopPropagation();
});

Full code:

$(".group").click(function(event) {

    // if ($(event.target).is('ul li ul li')) return false;

    $(".group").children("ul").animate({top: 0, opacity: 0}, 200);
    var liHeight = ($(this).children("ul").children("li").height())+20;
    var totalOptions = $(this).children("ul").children("li").length;
    var combinedHeight = totalOptions * liHeight;
    var slideIn = -(16+combinedHeight) // formula doing math on slidedistance
    $(this).children("ul").animate({
                            top: slideIn,
                            opacity: 1
                        }, 600);
});


$(".link").click(function(event) {
   event.stopPropagation();
});

I'd suggest an alternative way of the click listener. If you use the on method in jQuery - you can delegate the listener to the child element. Like so:

$('#buttons').on('click', 'li', function(e){
    var li = $(this);

    if(li.hasClass('group')){
        alert('group was clicked');
    }
    else if(li.hasClass('link')){
        alert('link was clicked');
        e.stopPropagation();
    }
});

A live example of this code is here - http://jsfiddle.net/skip405/YWzxB/

Notice: if you delete the stopPropagation part on the link delegation - you'll see two alerts. First - for the link and then - for the group.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top