Side navigation
#11115 closed bug (fixed)
Opened December 29, 2011 10:26PM UTC
Closed December 16, 2012 04:20AM UTC
".is()" and ".filter()" disagree on attribute selector "[checked]"
Reported by: | Pointy | Owned by: | gibson042 |
---|---|---|---|
Priority: | blocker | Milestone: | 1.9 |
Component: | selector | Version: | 1.7.1 |
Keywords: | 1.9-discuss | Cc: | |
Blocked by: | Blocking: |
Description
The selector "[checked]" is treated differently by ".is()" and ".filter()", in a strange and mysterious way when the "checked" property (not attribute) is set; that is, on checkbox inputs that in the DOM lack the "checked" attribute but which have been checked by user action (or possibly by explicit setting of the "checked" property). If you've got a jQuery object matching one DOM node (and just one), then ".is()" and ".filter()" are in agreement. That is, if a checkbox is checked only after the DOM is built, then both ".is()" and ".filter()" agree that the selector "[checked]" should not match the node.
However, when the jQuery object selects more than one checkbox, then the behavior of ".filter()" changes. Though each checkbox, if tested individually with ".is('[checked]')" would appear to not match, a call to ".filter('checked')" will nevertheless include them in the result if they've been dynamically updated.
Here is a jsfiddle to explore the issue:
Now I realize that "[checked]" is not really correct or useful or anything, but the behavior does seem inconsistent; I would imagine that any node that makes it through a selector-based ".filter()" should be guaranteed to pass a ".is()" test for the same selector. The funny thing is that the implementation boils down to calls to "Sizzle.matches()" or "Sizzle.matchesSelector()" (I think) and both of those are nearly identical.
Attachments (0)
Change History (18)
Changed December 30, 2011 02:46AM UTC by comment:1
_comment0: | Here's my take: \ \ In browsers that support it, Sizzle.matchesSelector is redefined to \ \ html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; \ \ When one of the checkboxes is run through elem.webkitMatchesSelector( "checked" ), the result is false. \ \ \ When the code is run through Sizzle's ATTR filter, it hits https://github.com/jquery/sizzle/blob/423f35af8bf43f3f07bb17284e21608f08372c22/sizzle.js#L860-861 which is actually checking the property, so it returns true. \ \ If your jQuery object has more than one element, it'll follow the "normal" Sizzle path which happens to check the elements' property. With a single element, it follows the .matchesSelector path which has been shown to return false. \ \ Bit longwinded, but that was interesting :) \ \ A simple workaround might be to use the :checked pseudo selector which always checks the property rather than the attribute. → 1325213251318101 |
---|
Changed December 30, 2011 03:08AM UTC by comment:2
Totally agree that this is an academic issue, but it's pretty hard to think of it as anything but incorrect behavior.
Changed December 30, 2011 03:41PM UTC by comment:3
component: | unfiled → selector |
---|---|
priority: | undecided → low |
status: | new → open |
Removing the boolhook would fix this issue, but I don't think we can remove it for a while. Nevertheless, the ideal way to check if a checkbox is checked in a selector is with http://api.jquery.com/checked-selector/.
Changed September 17, 2012 05:50PM UTC by comment:5
keywords: | → 1.9-discuss |
---|
Changed September 24, 2012 05:06PM UTC by comment:6
So are we going to attempt to remove boolHook in 1.9? If not I think we have to assume boolHook will be there forever and this will need to be fixed some other way or closed wontfix.
Changed September 27, 2012 12:08PM UTC by comment:7
boolHook has been on notice since 1.6, right? I think it's time.
Changed October 26, 2012 01:21PM UTC by comment:8
+1, I'm not for removing the boolHook, but I am for making is and filter consistent.
Changed October 29, 2012 05:27PM UTC by comment:10
milestone: | None → 1.9 |
---|
Changed November 19, 2012 05:28PM UTC by comment:11
owner: | → timmywil |
---|---|
status: | open → assigned |
Changed November 21, 2012 08:54PM UTC by comment:12
#12830 is a duplicate of this ticket.
Changed November 21, 2012 11:56PM UTC by comment:13
I did some investigating, and I'm guessing this is an issue with querySelectorAll(). It seems that "[selected]" and "[checked]" return elements that initially had this attribute on page load. This is only an issue when used like "option[selected]" since jQuery selects that using querySelectorAll() when available. When using filter("[selected]"), it is not using querySelectorAll(), and therefore returns the correct results.
Changed November 22, 2012 04:44AM UTC by comment:14
Replying to [comment:13 Allen Schmidt <cobrasoft@…>]:
I did some investigating, and I'm guessing this is an issue with querySelectorAll(). It seems that "[selected]" and "[checked]" return elements that initially had this attribute on page load. This is only an issue when used like "option[selected]" since jQuery selects that using querySelectorAll() when available. When using filter("[selected]"), it is not using querySelectorAll(), and therefore returns the correct results.
You've got it exactly backwards... [selected]
is an ''attribute'' selector, and jQuery incorrectly treats elements having true
analogous ''properties'' as matching it, while querySelectorAll
correctly ignores them.
Changed November 29, 2012 05:57AM UTC by comment:15
_comment0: | (In #12600) The expectations in the comments of that fiddle are erroneous... `select[value=…]` shouldn't match anything, because most select elements—and those in particular—don't have attributes named "value". \ \ This ticket is the `valHook` equivalent of #11115: native Sizzle always gets it right, jQuery only does so when `jQuery.filter` calls a qSA-backed `matchesSelector` in the single-element case: http://jsfiddle.net/Mj23Q/43/ → 1354169136709145 |
---|---|
blockedby: | → 12600 |
(In #12600) The expectations in the comments of that fiddle are erroneous... select[value=…]
shouldn't match anything, because most select elements—and those in particular—don't have attributes named "value".
This ticket is the attrHooks
equivalent of #11115: native Sizzle always gets it right, jQuery only does so when jQuery.filter
calls a qSA-backed matchesSelector
in the single-element case: http://jsfiddle.net/Mj23Q/43/
Changed December 04, 2012 05:00AM UTC by comment:16
priority: | low → blocker |
---|
The hook should be moved to https://github.com/jquery/jquery-compat/.
Changed December 10, 2012 08:59AM UTC by comment:17
owner: | timmywil → gibson042 |
---|
Changed December 16, 2012 04:20AM UTC by comment:18
resolution: | → fixed |
---|---|
status: | assigned → closed |
Fix #11115: Normalize boolean attributes/properties. Close gh-1066.
Changeset: a763ae72775f69509d0a8a6b50702bcf2c7e6fbf
Here's my take:
In browsers that support it, Sizzle.matchesSelector is redefined to
html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
When one of the checkboxes is run through elem.webkitMatchesSelector( "[checked]" ), the result is false.
When the code is run through Sizzle's ATTR filter, it hits https://github.com/jquery/sizzle/blob/423f35af8bf43f3f07bb17284e21608f08372c22/sizzle.js#L860-861 which is actually checking the property, so it returns true.
If your jQuery object has more than one element, it'll follow the "normal" Sizzle path which happens to check the elements' property. With a single element, it follows the .matchesSelector path which has been shown to return false.
Bit longwinded, but that was interesting :)
A simple workaround might be to use the :checked pseudo selector which always checks the property rather than the attribute.