views:

98

answers:

2

Let's say my HTML may look one of two ways:

Option 1:

<h2>My header
    <span class="apple">My Span</span>
</h2>

Option 2:

<h2>My header</h2>

Via jAuery, I want to append a span to either the nested span (if it's there) or to the header directly, resulting in one of these:

Option 1:

<h2>My header
    <span class="apple">My Span
        <span>My Span Inserted via jQuery</span>
    </span>
</h2>

Option 2:

<h2>My header
    <span>My Span Inserted via jQuery</span>
</h2>

Is there a clever selector that would detect both of the above scenarios? I could, of course, first check to see if my header has a span with a class of 'apple' within it. If so, do this, else, do that. I was wondering if there was a more elegant and less verbose solution, though.

+3  A: 

I think using a children filter and then andSelf() would work to get both. You could then use the first to choose the correct one. If children is empty then first will be the only element, that is, the header.

$('h2').each( function() {
    $(this).children('span')
           .andSelf()
           .filter(':first')
           .append('<span>My Span Inserted via JQuery</span>');
});

Updated to use filter() (instead of the second find).

tvanfosson
thanks! .andSelf() looks like the perfect solution. I can see lots o fuses for that.
DA
Can't seem to get this to work, it does not seem to match a simple h2? see http://jsbin.com/imico/edit
Morningcoffee
this does NOT work and is the wrong answer .. @Morningcoffee got it right
Scott Evernden
@Scott -- the second find() should have been a filter(). Corrected.
tvanfosson
+7  A: 

The following selector selects all <h2>'s that does not have a <span> element or a <span> element which has <h2> as parent.

$("h2:not(:has(span)), h2 > span")
      .append("<span>My Span Inserted via jQuery</span>");
Morningcoffee
I like that you you can operate on the on the set instead of having to foreach through each one.
Mark
THIS is the .. correct answer
Scott Evernden
Realize that this will iterate over the entire DOM twice, once for each selector -- Sizzle gets invoked on the entire document once per selector. My answer iterates over the DOM once.
tvanfosson
I was going for his "less verbose" criteria, he didn't really mention performance as an issue. Your solution is faster, but I would like to believe that mine is less obtrusive.
Morningcoffee
Thanks Morningcoffee. That's definitely less verbose. Will have to factor in the performance issue, but this is a good option too!
DA