The reason for this behavior is that your colon breaks the selector into querySelectorAll because it is not valid.
As such, it by default uses Sizzle, which will tolerate the colon, even if it is not technically supported (which means that it may break in the future). Sizzle will check both attributes and properties. Thus, it will not find the text attribute, but it will find the text property of the <option> element.
Here is an example that demonstrates that Sizzle will match a property, not just an attribute with its attribute-equals selector.
Code from the 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 referred to another example because I dialed the wrong version number. Fixed.
user113716
source share