javascriptcssmootoolscss-selectors

CSS selector for targeting only immediate children and not other identical descendants


I have a nested sortable list that can have items dynamically added or removed and can be nested n-levels deep. On nesting, a new ul element is injected into whatever li element is selected to be the parent. The initial state of the list is something like the following:

<ul id="parent">
    <li id="One"><a href="" class="listLink"><span class="position">1</span>One</a></li>
    <li id="Two"><a href="" class="listLink"><span class="position">2</span>Two</a></li>
    <li id="Three"><a href="" class="listLink"><span class="position">3</span>Three</a>
        <ul>
            <li id="A"><a href="" class="listLink"><span class="position">1</span>A</a></li>
            <li id="B"><a href="" class="listLink"><span class="position">2</span>B</a></li>
            <li id="C"><a href="" class="listLink"><span class="position">3</span>C</a></li>
            <li id="D"><a href="" class="listLink"><span class="position">4</span>D</a></li>
            <li id="E"><a href="" class="listLink"><span class="position">5</span>E</a></li>
            <li id="F"><a href="" class="listLink"><span class="position">6</span>F</a></li>                    
        </ul>
    </li>   
    <li id="Four"><a href="" class="listLink"><span class="position">4</span>Four</a></li>
    <li id="Five"><a href="" class="listLink"><span class="position">5</span>Five</a></li>
    <li id="Six"><a href="" class="listLink"><span class="position">6</span>Six</a></li>                    
</ul>

I'm using MooTools to do the sort, etc., and it works fine, but I'm having trouble resetting the position text correctly on sort. Every CSS selector I try to use also includes all of the children rather than just the li elements that belong in the list and not any belonging to sublists. Assume that except for id, position, and text, each li element in all lists is identical to all others. Is there a selector for getting only the immediate children? Is there another way to do this?

I tried some child selectors like the ones mentioned:

Here is the function that I'm currently having run when an item is dropped, which doesn't handle the sorting, which works fine, just updating the position. Note that it is also MooTools syntax:

var drop = function(el){
    el.getParents('ul').reverse().each(function(item){
        var posCount = 1;
        item.getElements('li a span[class=position]').each(function(pos){
            pos.set('text', posCount);
            posCount++;
        });
    });
}

Currently, changing any item order on the main level will renumber everything 1-12, even the sublists. Changing any item on a sublist will give the correct numbers for that list, but cause the parent lists to incorrectly count all child li elements in numbering.

I feel like this is an ugly hack, but it works:

var drop = function(){
    var ulCount = 1;
    $$('ul').each(function(item){
        if(item.get('id') != 'parent') { 
            item.set('id', 'id-'+ulCount);
        }
        var elId = item.get('id');
        var posCount = 1;
        $(document).getElements('#'+elId+'>li>a>span[class=position]').each(function(pos){
            pos.set('text', posCount);
            posCount++;
        });
        ulCount++;
    });
}

Solution

  • ul > li
    

    only does the immediate children. So, for example, to do only the top level list elements you could use:

    #parent > li
    

    Note: this isn't supported on IE6.

    The common workaround for backwards compatibility is to do something like this:

    #parent li { /* style appropriately */ }
    #parent li li { /* back to normal */ }
    

    It's more tedious because you have to apply styles and then turn them off (and you may not necessarily know what the old values are) but it's the only IE6-friendly pure CSS workaround there is.

    Edit: Ok you have a MooTools specific issue. getElements() returns all descendants, not just immediate children. Try using getChildren().

    var drop = function(el){
        el.getParents('ul').reverse().each(function(item){
            var posCount = 1;
            item.getChildren("li").getElements("a span[class=position]").each(function(pos){
                    pos.set('text', posCount);
                    posCount++;
            });
        });
    }
    

    or something like that.