views:

77

answers:

4

I have a multi-level lists like this:

<ul>
    <li>Item 1
        <ul>
            <li>Item 1's 1st Child
                <ul>
                    <li>Item 1's 1st Grandchild
                        <ul>
                            <li>Item 1's Grand Grandchild</li>
                        </ul>
                    </li>
                    <li>Item 1's 2nd Grandchild</li>
                    <li>Item 1's 3rd Grandchild</li>
                </ul>
            </li>
            <li>Item 1's 2nd Child</li>
            <li>Item 1's 3rd Child</li>
        </ul>
    </li>
    <li>Item 2</li>
</ul>

I want for each li to have class 'level' according to their positions. The result would be like this:

<ul>
    <li class="level-1">Item 1
        <ul>
            <li class="level-2">Item 1's 1st Child
                <ul>
                    <li class="level-3">Item 1's 1st Grandchild
                        <ul>
                            <li class="level-4">Item 1's Grand Grandchild</li>
                        </ul>
                    </li>
                    <li class="level-3">Item 1's 2nd Grandchild</li>
                    <li class="level-3">Item 1's 3rd Grandchild</li>
                </ul>
            </li>
            <li class="level-2">Item 1's 2nd Child</li>
            <li class="level-2">Item 1's 3rd Child</li>
        </ul>
    </li>
    <li class="level-1">Item 2</li>
</ul>

Is there a way to achieve this with jQuery?

Thank you very much for your attention.

+2  A: 

Assuming you only have 4 levels of lists

$("ul > li").addClass("level-1"); 
$("li.level-1 > ul > li").addClass("level-2"); 
$("li.level-2 > ul > li").addClass("level-3"); 
$("li.level-3 > ul > li").addClass("level-4");

Maybe there's a more programmatic way of doing this & allowing for arbitrary depth, but this was quick.

Pickle
Unfortunately, I need it to run without limitations to the depth.
Firewalker06
+2  A: 

Think this is a generic solution:

var current = $('ul:first');
var depth = 1;

while (current.size()) {
  current = current.children('li').addClass('level-' + depth++).children('ul');
};
Matt
I just tried this too, and it's slightly faster than each() method. About 3ms differences. I guess this is the best answer for performance and simplicity.
Firewalker06
I like it! But, having current combine the child arrays is not the most intuitive mechanism. It's very easy to forget just how powerful Jquery's selectors can be!
David
@David: What jQuery selector are you thinking of using?
Matt
I think that comment didn't read as quite what I meant.I'm just amazed sometimes to see something like this- very powerful, and simple once you understand what it's doing.The part that actually threw me was the "depth++". It took a bit to remember that the addClass operates on all of the child(li) all at the same time, rather than on consecutive elements of the child array. On my first quick read, it looked like you should have wound up with the final li having a class of "level-9".
David
+3  A: 

Not the quickest because of DOM traversals, but, you could do it with as little as:

​$("​​​li").each(function() {
   $(this).addClass("level-" + ($(this).parents("li").length+1)); 
});​​​

You can see a working example here

Nick Craver
Great, I have tried it and it works. And without restriction to how many levels it should have. This is the simplest method that I wanted.
Firewalker06
A: 

I'll play... you can always make it recursive :)

$(function() {
    addLevel($('ul:first'));
});

function addLevel($ul, level) {
    ( ! level ) ? level = 1 : level += 1;
    $ul.children('li').each(function() {
        $(this).addClass('level-' + level);
        $(this).children('ul').each(function() { addLevel($(this), level) });
    });
}
David