Nathan Osman
Nathan Osman

Reputation: 73305

How to select the first sub-item in a list with jQuery?

I know this is a really strange title, but I'll do my best to explain the problem below:

I have a page with a layout similar to the following:

.item
  pre
    code
  pre
    code
.item
.item
  pre
    code

I want to select the code items (because I want to loop through them with 'each()'). However, I want to only select the first 'pre/code' combo in each .item block.

How can I do this?

At first I had $(".item pre code"), which selects all of the 'pre/code' combos. I noticed in the jQuery docs that there's a :first-child selector. I tried sticking it on the end of the pre, but that didn't work.

Upvotes: 1

Views: 573

Answers (1)

lonesomeday
lonesomeday

Reputation: 238115

You need to use the nth-child selector. This chooses the element that matches the argument (a 1-based index) for each of the parent selectors. (:first only returns the first pre; nth-child returns all pres that are the first child of their parent:

$('.item pre:nth-child(1) code')

http://jsfiddle.net/syLMD/

NB that this is equivalent to .item pre:first-child code. As far as I can tell, that works just fine.


OK, with the knowledge that the first pre may not contain a code... You can't do this with a simple selector. This needs more complex filtering:

$('.item')
    .data('found', false) // ensure that all item nodes have found = false
    .find('pre code') // find descendant code nodes
        .filter(function(){
            if ($.data(this.parentNode.parentNode, 'found')) { // if this item has already been found
                return false; // remove the code element from the selection
            } else {
                $.data(this.parentNode.parentNode, 'found', true) // mark the item as found
                return true; // keep the code element in the selection
            }
        })
            .each(function(){ // for each of these code elements
                $('#output').append(this.innerHTML); // or something else
            })
        .end() // back to the un-filtered selection of code elements
    .end() // back to the selection of div.item elements
    .data('found', false); // mark them as not found, for the next time

See example. Note that this isn't exactly going to have wonderful performance...

Upvotes: 2

Related Questions