soyuka
soyuka

Reputation: 9105

Finding the children clicked nodeName within the parent element

Consider this simple html list :

<ul>
    <li><a href="http://google.com/">Test</a></li>
    <li><a href="http://google.com/">Test2</a>
        <ul>
            <li><a href="http://google.com">Test2 - 1</a></li>
            <li><a href="#3">Test2 - 2</a></li>
        </ul>
    </li>
</ul>

Now this 3 jQuery lines which makes it collapsible :

$('ul > li').on('click', function(e) {
    e.preventDefault();
    $(this).find('ul').slideToggle(); 
});

The matter here is that if a link has been clicked in the deeper ul I wanted it to follow the link, which in jQuery will be :

if(this.nodeName != 'A') {
    e.preventDefault();
}

At the moment the nodeName is LI and isn't the last clicked child element.

Question : Is it possible to do this without having a second event handler for deeper elements ? (i.e : $('ul > li ul li a').click())

I tried e.stopPropagation and a lots of other css selectors (ul > *, ul li etc.) without success.

Upvotes: 0

Views: 209

Answers (3)

Alepac
Alepac

Reputation: 1831

Try this:

$('li > ul').prev().on('click', function(e) {
    e.preventDefault();
    $(this).next().slideToggle();
});

And here the link to the jsfiddle.Here the li > ul selector find each ul child of li then .prev() selects the previosu sibling (i.e. the a tag). This way we can prevent the default behaviour of a tag (i.e. following the link) and slideToggle the next (i.e. the ul)

Upvotes: 1

soyuka
soyuka

Reputation: 9105

After some researches I came to this answer :

$('ul > li').on('click', function(e) {
     e.stopPropagation();

    if(!$(this).find('ul').length == 0) {
         e.preventDefault();
         $(this).find('ul').slideToggle();     
    } 
});

So it'll follow links if no ul element is founded in the clicked element. If you've got a better solution please share :).

JSFiddle here.

This solution won't work with a specified ul by ID :

$('ul#test > li').on('click', function(e) {
     e.stopPropagation();

    if(!$(this).find('ul').length == 0) {
         e.preventDefault();
         $(this).find('ul').slideToggle();     
    } 
});

See updated fiddle here, can somebody explain why ?

Ok thanks to Simon I've corrected with my listener on the li from the ul#test :

$('ul#test').on('click', 'li', function(e) {
     e.stopPropagation();

    if(!$(this).find('ul').length == 0) {
         e.preventDefault();
         $(this).find('ul').slideToggle();     
    } 
});

Wonderful !

Upvotes: 0

techfoobar
techfoobar

Reputation: 66663

Since your root selector is 'ul > li' it will match nothing but LIs. i.e. There is no way you'll be matching clicks to other elements inside a handler bound using that selector.

What you can do instead is a more generic selector and filter inside the handler:

For example:

$('ul *').on('click', function(e) {

    // leaf LIs only
    if($(this).is('li') && $(this).children().length == 0) {
        e.preventDefault();
        $(this).find('ul').slideToggle();  
    }

});

OR, You can combine the selectors and handle it using one handler like:

$('ul > li, ul > li ul li a').on('click', function(e) {
    if($(this).is('a')) return true; // follow links
    e.stopPropagation();
    $(this).find('ul').slideToggle(); 
}

Upvotes: 0

Related Questions