views:

39

answers:

2

I'm trying to find a jQuery selector that will match only the element that directly contains the text "PDT" in the following two examples (truncated for brevity):

<div>
  <p>
    <strong>(07-27) 21:28 PDT SAN FRANCISCO</strong> -- San Francisco
    supervisors ended more than a decade...
  </p>
  <p>The 10-1 vote, with only Supervisor Chris Daly dissenting</p>
</div>

and

<div>
  <p>(07-28) 08:59 PDT WASHINGTON --</p>
  <p>Orders to U.S. factories for big-ticket manufactured goods fell...</p>
</div>

In the first case, this would be the <strong>, and in the second case, the first <p>. I'm looking for a single selector that would match both, or multiple selectors that wouldn't find any false positives.

By my analysis, I know the following things about the elements that would match:

  • they can either be a <p> or a <strong> element
  • they're the first child of their parent
  • they contain the text "PDT"
  • they have no children

Given all of these attributes, I think there should be a jQuery selector that will find these elements with high accuracy, and few to no false positives. However, I'm having some trouble putting one together, as I haven't used a selector this complex anymore.

The tools I'm aware of are (each linked to documentation):

I'm not sure if the last one will be useful - I just read the documentation more carefully and noticed that text nodes are considered by the :empty selector, so HTML elements that I'm trying to target aren't actually empty. However, I can rely on the fact that they'll only contain one text node, and nothing else.

My first shot was:

*:contains(PDT):first-child:empty

but that didn't work. I've tried several other variations, but I'm kind of stumped. Can anyone offer some guidance?

One caveat: I'm not actually using jQuery in my script, but rather its Sizzle selector engine. Therefore, I need to try to do this all in one selector, and I can't use jQuery methods like .find() and .filter().

Thanks in advance for any help!

+1  A: 
$('strong:contains("PDT"):first-child, p:contains("PDT"):first-child')

This can obviously be optimized based on ids and context.

meder
Thanks, meder! I ended up going with patrick dw's approach since it's more specific and should decrease the chance of false positives. Your help is much appreciated, though!
Bungle
+3  A: 

Using all your criteria, you could do:

Try it out: http://jsfiddle.net/CKuYD/

var $result = $(':first-child:not(:has(*)):contains(" PDT ")');

Note the spaces around " PDT ".

This will return if they contain the text, are a first-child, and don't have any other child tags.

patrick dw
wow +1 - I can't imagine myself coming up with that selector ever. One thing I might add to the selector is to repeat the above with `p` and `strong` prefixed to satisfy the first criteria.
Anurag
@Anurag - Agreed, if OP doesn't care how long it gets, it would probably be good to repeat it with the specific tag names. `$('p:first-child:not(:has(*)):contains(" PDT "), strong:first-child:not(:has(*)):contains(" PDT ")');` But frankly, with the spaces around the `" PDT "`, I doubt there will be any false positives.
patrick dw
@patrick dw - Thanks so much. This works like a charm! I ended up using the more verbose pair of selectors based on Anurag's suggestion - I don't think I can actually count on the trailing space after `PDT` in all cases, so the extra specificity should help.
Bungle