7

Given a SELECT element:

<select>
    <option>foo</option>
    <option>bar</option>
    <option>baz</option>
</select>

I want to select the OPTION element with the value "bar".

This doesn't work:

$('option[text="bar"]').attr('selected', true);

However, this does work:

$('option:[text="bar"]').attr('selected', true);

Why?

Live demo: http://jsfiddle.net/YbfqZ/2/

Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
  • Remember, jQuery changed in the last few version. So you don't quote attributes input[name=dog_type]:radio. It used to look like this input[@name="dog_type"]. There's a lot of old tutorials that still have quotes and @ signs that no longer work. – Tobias Feb 15 '11 at 21:24
  • 7
    @Gaspy No, it's the opposite. Before, quotes were optional, now they are mandatory. It says it in the docs: http://api.jquery.com/attribute-equals-selector/ – Šime Vidas Feb 15 '11 at 21:37
  • Well I really put my foot in my mouth. I had a problem yesterday when I took some old jQuery code and though I had fixed it by removing the quotes. – Tobias Feb 16 '11 at 06:23

2 Answers2

8

The reason for that behavior is that your colon breaks the selector for querySelectorAll because it isn't valid.

As such, it defaults to Sizzle, which will tolerate the colon, even though it technically isn't supported (which means it could break in the future). Sizzle will check for both attributes and properties. As such, it won't find a text attribute, but it will find the text property of the <option> element.

Here's an example that demonstrates that Sizzle will match a property instead of just an attribute with its attribute-equals selector.


Code from example:

  // set a custom property on the last option
$('#id option').slice(-1)[0].customProp = 'customValue';

  // breaking the selector with : we default to Sizzle,
  //    which matches our custom property
$('#id option:[customProp="customValue"]').attr('selected', true);

EDIT: My example link previously referenced someone else's example because I typed the wrong revision number. Fixed.

user113716
  • 318,772
  • 63
  • 451
  • 440
  • However, why doesn't jQuery recognize the selector without the colon? In both [CSS2](http://www.w3.org/TR/CSS2/selector.html#pattern-matching) and [CSS3](http://www.w3.org/TR/2001/CR-css3-selectors-20011113/#selectors), the quotes belong in the selector, and the colon doesn't. – eykanal Feb 15 '11 at 21:22
  • @jAndy: Thanks. Funny thing is that I *just* left this as a comment for [this answer](http://stackoverflow.com/questions/5009179/jquery-traversing-select-option-text/5009231#5009231), then saw that Šime made it a question. – user113716 Feb 15 '11 at 21:22
  • 1
    @eykanal: That's the point. *Without* the colon, it is a valid selector, so Sizzle defaults to `querySelectorAll`, which only looks for *attributes* (as it should). Sizzle muddles the distinction between *attributes* and *properties*, and will match either. – user113716 Feb 15 '11 at 21:24
  • @patrickdw: Does that make this a bug in Sizzle, or is Sizzle not compliant by default, to allow for sloppier matching? – eykanal Feb 15 '11 at 21:27
  • 2
    @eykanal: Good question. I don't know if it's a bug. I mean I suppose you could consider it allowing the misplaced `:` to be a bug, but then it's a bug that prevents broken code. With regard to matching properties instead of just attributes, I guess that must have been their intention. There are many Sizzle features that are not valid CSS. `:eq() :has() :first :last` to name a few. I tend to avoid them, because if I plan my code around only valid selectors, I'll end up with better performance overall in browsers that support `qsa`. – user113716 Feb 15 '11 at 21:34
  • 1
    @Šime Vidas: Indeed it is. I'm slowly learning to first run a test against `qsa` if I ever experience odd or inconsistent behavior in a selector. – user113716 Feb 15 '11 at 21:36
2

The correct way to do this is to give the SELECT an id, and give the OPTION items values. Then you can set the select's value.

<select id="theSelect">
    <option value="foo">foo</option>
    <option value="bar">bar</option>
    <option value="baz">baz</option>
</select>

And the JS would look like this

$('#theSelect').val('foo');

Live Demo http://jsfiddle.net/YbfqZ/4/

Dutchie432
  • 28,798
  • 20
  • 92
  • 109