Search code examples
jqueryslidetogglenested-listsevent-delegation

jquery delegation on nested ul elements expand/collapse features


I have a nested ul "tree" that, for the sake of this question, is only a couple levels at the top and a couple children, but if you could assume that from the top ul (targeted with id="group" here) that there could be several nested elements, but they will always follow this structure. At the moment I only have the top elements collapsing their children ul elements on collapse. I want to achieve this nature throughout all of the ul elements. Furthermore, I want to also collapse open elements "at the same level" of another clicked so that only one ul with another is open. I hope I've explained the feature I'm trying to accomplish. I feel like I should be using slideToggle() but every attempt with it has ended poorly... I should also say that this is my first hand at actually using delegate() but I understand the propagation ("event bubbling") and is part of the reason I feel like it's the way to go for such a task. The other being that I may need to append new elements into the ul. So here's what I have currently:

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
<script type="text/javascript">
    $(function(){
        $('ul#group').delegate('a', 'click', function(event) {
            if($(event.target).next().is(':visible')) {
                if($(event.target).next()) {
                    $(event.target).next().find('ul').slideUp(250, function() {
                        $(event.target).next().slideUp(250);
                    });
                }
            } else {
                $(event.target).next().slideDown(250);
            }
        });
    });
</script>
<ul id="group">
    <li>
        <a href="#" onclick="return false;">Foo Bar 1</a>
        <ul>
            <li>
                <a href="#" onclick="return false;">Foo 1</a>
                <ul>
                    <li>
                        <a href="#" onclick="return false;">Bar 1</a>
                    </li>
                </ul>
            </li>
            <li>
                <a href="#" onclick="return false;">Foo 2</a>
                <ul>
                    <li>
                        <a href="#" onclick="return false;">Bar 2</a>
                    </li>
                </ul>
            </li>
        </ul>
    </li>
    <li>
        <a href="#" onclick="return false;">Bar Foo 2</a>
        <ul>
            <li>
                <a href="#" onclick="return false;">Bar 1</a>
                <ul>
                    <li>
                        <a href="#" onclick="return false;">Foo 1</a>
                    </li>
                </ul>
            </li>
            <li>
                <a href="#" onclick="return false;">Bar 2</a>
                <ul>
                    <li>
                        <a href="#" onclick="return false;">Foo 2</a>
                    </li>
                </ul>
            </li>
        </ul>
    </li>
</ul>

And here's a jsfiddle link to the test case. All input is welcome to the best way this can be accomplished.


Solution

  • Is something like this what you're after: http://jsfiddle.net/GSEDy/3/

    I made some edits.

    $('ul#group').delegate('a', 'click', function(event) {
        if ($(event.target).next().is(':visible')) {
            $(event.target).next().find('ul').slideUp(250, function() {
                $(event.target).next().slideUp(250);
            });
        } else {
            $(event.target).parent().siblings().find("ul").slideUp(250);
            $(event.target).next().slideDown(250);
        }
    });​