Bug Tracker

Ticket #4283 (closed bug: fixed)

Opened 6 years ago

Last modified 2 years ago

.attr('checked') & XHTML 1.1 Strict

Reported by: andrew_ Owned by: timmywil
Priority: major Milestone: 1.6
Component: attributes Version: 1.5.2
Keywords: attr checked xhtml firefox Cc:
Blocking: Blocked by:

Description

When dealing with XHTML 1.1 Strict, Firefox 3.0.6 and a content type of application/xhtml+xml, the attr() method working with 'checked' does not function as previous versions of jQuery have. The following calls/parameters do not function as expected;

.attr('checked', false); .attr('checked', ); .removeAttr('checked');

The following html can be saved as an .xhtml file an opened locally within firefox, or placed on a server which is configured to send the content-type for xhtml correctly.

<!DOCTYPE html PUBLIC "-W3CDTD XHTML 1.1EN" " http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns=" http://www.w3.org/1999/xhtml" xml:lang="en"> <head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript" src=" http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> <script type="text/javascript">

$(document).ready(function(){

$('#_Check').click(function(){

$(this).attr('checked', false); does not work $(this).attr('checked', ); does not work $(this).removeAttr('checked'); does not work this.checked = false; works

});

});

</script>

</head> <body>

<input type="checkbox" id="_Check" /><label for="_Check">Click Me</label>

</body> </html>

Attachments

foo.xhtml Download (790 bytes) - added by andrew_ 6 years ago.
test case
faqtest.xhtml Download (707 bytes) - added by andrew_ 6 years ago.
XHTML 1.0 Strict Test Case
4283 Temporary Fix.txt Download (815 bytes) - added by andrew_ 5 years ago.
Temporary Fix - Not a Patch

Change History

Changed 6 years ago by andrew_

test case

comment:1 Changed 6 years ago by andrew_

For what it's worth, I have also been able to reproduce the incorrect behavior for the 'selected' attribute on <option> elements within <selects>s.

comment:2 Changed 6 years ago by john

  • Status changed from new to closed
  • Resolution set to wontfix
  • Component changed from unfilled to core

This is the correct behavior. If you're serving up your document as XML you need to treat it like a proper XML document - which means only modifying the actual DOM attributes (and not the expandos).

See here for more information:  http://docs.jquery.com/Frequently_Asked_Questions#How_do_I_check.2Funcheck_an_input.3F

comment:3 Changed 6 years ago by andrew_

  • Status changed from closed to reopened
  • Resolution wontfix deleted

You would be serving the document as xhtml+xml. Not pure xml. The xhtml spec clearly defines hundreds of attributes which are valid for tags. Prior to 1.3.2, this functioned as expected.

Additionally, the link you provided is negated by the test case provided when the ticket was opened. removeAttr() does not uncheck a checkbox in this case.

I dont feel that this was given enough consideration. (And suspect the test case wasnt even examined) We're not setting the checked property with an expando here, the browser is doing it via the click event, as it's default behavior mandates. Once the user has interacted with the checkbox and effectively 'checked' it, the provided attr() and removeAttr() methods do not have an effect on 'unchecking' the checkbox.

comment:4 Changed 6 years ago by andrew_

Additionally I have tested the same test case xhtml document as was attached in the following browsers;

Firefox 3.0.6 - Fails Opera 9.64 - Passes Chrome 1.0.154.48 - Fails Safari 4 Public Beta (528.16) - Fails IE 7 - Passes (but fails to recognize XHTML, so is moot)

I have done more investigation into this using tools such as Firebug. What I am finding is that the call to removeAttribute at line 1222 would appear to succeed, as my debugging tools are no longer reporting/showing the 'checked' attribute within the DOM inspector. However, the checkbox is not being 'unchecked'. It is only when the expando ".checked = false" is explicitly being set that the checkbox will be unchecked.

I have also run across this page which seems to be linked to a JQuery FAQ;  http://www.nabble.com/Bug-in-FAQs-for-checkboxes-td21648503s27240.html Although the example provided here is using jQuery 1.3, the result is the same. Changing the script tag to use 1.3.2 yields the same result;

Manually check the 'I'll be checked/unchecked' checkbox (via a mouse click on the checkbox), and then click the 'Uncheck - good' button. That button uses the removeAttr method and fails to uncheck the checkbox for the browsers listed above as 'Failed'. (I have attached faqtest.xhtml as a separate test case). As a point of reference, this example uses the XHTML 1.0 Strict spec, and not 1.1 as the previous example had, leaning towards this as a general XHTML issue.

For this situation, would it not be valid to examine the object at hand to see if an expando method exists, and attempt to intelligently degrade to use that expando via attr()?

Changed 6 years ago by andrew_

XHTML 1.0 Strict Test Case

comment:5 Changed 6 years ago by andrew_

Please excuse the multiple posting, as there seems to be no way to edit.

Upon further investigation I found the following;

1219 removeAttr: function( name ) { 1220 jQuery.attr( this, name, "" ); 'checked' attribute does not exist in attributes collection 1221 if (this.nodeType == 1) 'checked' attribute created by previous call and added to the attributes collection 1222 this.removeAttribute( name ); 1223 }, 'checked' attribute removed by removeAttribute

This execution path would seem to confirm that the actual checked state of the checkbox is not effected by removeAttr.

comment:6 Changed 6 years ago by dmethvin

Andrew, I spent about 45 miniutes looking at your test case and thinking about the implications last night, then posted to the jQuery Dev list. So it's definitely gotten some consideration.

jQuery is doing exactly what you asked and no more, setting and getting *attributes*. If you use the browser's setAttribute and removeAttribute methods they exhibit the exact same behavior. I know, because I experimented with your test case.

For this situation, would it not be valid to examine the object at hand to see if an expando method exists, and attempt to intelligently degrade to use that expando via attr()?

jQuery does many hacky things in the name of compatibility for HTML documents. It seems that one goal of XHTML+XML is to require a high level of precision on the part of the designer/developer to say precisely what they mean with no sloppiness. There are significant gotchas to trying to read minds.

Just before your ticket last night, I looked at #4281 which is the other side of this coin. That person wants to know why .attr("onclick", "javascriptcode") only works once; it's due to the browser-induced magic of attributes becoming properties. The same goes for an element's style property (a CSSStyleDeclaration object) versus its style attribute (a string, if it's specified at all).

It seems like this case would benefit from a $().prop() method that you could use that when you know for sure that you need to get/set a property and not an attribute. If you want the old fuzzy do-what-I-mean behavior subject to all the caveats, just serve the document as HTML. IE is going to do that anyway, so I am not sure of the benefits of using XHTML+XML at this point.

comment:7 Changed 6 years ago by andrew_

jQuery is doing exactly what you asked and no more, setting and getting *attributes*. If you use the browser's setAttribute and removeAttribute methods they exhibit the exact same behavior. I know, because I experimented with your test case.

Confirmed, and I agree. I hope that my posts have been interpreted more as an 'unexpected behavior' more than a 'fault of jquery'.

It seems like this case would benefit from a $().prop() method that you could use that when you know for sure that you need to get/set a property and not an attribute.

This would be fantastic.

IE is going to do that anyway, so I am not sure of the benefits of using XHTML+XML at this point.

(This is quite the tangent, but it offers explanation) I took over a development lead for the UI of an application my company is developing with a team overseas. The team overseas has very little understanding about why good html structure is important and how the browser(s) interprets the html that [they] are given. The html in this project was nothing short of tag soup, and had so many errors it was mind blowing. We decided to move ahead with XHTML in order to enforce runtime validation (thank you Firefox) so any mistakes that were made were quite obvious, allowed us to fix the previous mistakes quite easily given the dynamic nature of the site and also put into place an accountability measure. On the whole it was a good move for us. If it were not for those reasons, we would still be serving HTML 4.0.

Thanks for the time you've given to this issue and for examining the test cases I attached. Your consideration of the issue is much appreciated.

comment:8 Changed 5 years ago by andrew_

@ dmethvin

Just following up to see if there has been any progress with this.

Changed 5 years ago by andrew_

Temporary Fix - Not a Patch

comment:9 Changed 5 years ago by andrew_

I've uploaded a temporary fix by proxying attr and remoteAttr. It's not an ideal solution or fix. But for folks with the server sending application-xhtml+xml content type correctly, this will work.

comment:10 Changed 5 years ago by alex_sh

I'm affected by the same problem.

The thing is, setAttribute() / removeAttribute() (and, by extension, jquery's attr()) don't guarantee that the attribute value will actually change the widget state. For that, properties must be used. However, this is highly impractical because there seems to be no way to mass-apply the property values on jquery-selected elements.

Also, note that the types are different. For example, to disable the input element, the "disabled" attribute value must be set to "disabled" (works for both html and xhtml), while the property is boolean, and must be set to true.

$().prop() (or something like that) would be highly welcome. Something like $(":input").prop("disabled", true).

This affects properties like disabled, checked, selected, readOnly (the attribute is named readonly), etc...

This change would allow people to use the same scripts for xhtml and html, thus allowing them to serve them to non-xhtml-compliant user agents.

comment:11 Changed 5 years ago by James Tindall

This really needs fixing. $(':input').attr('checked', 'checked') does not add the attribute at all. How can this not working be expected behaviour?

comment:12 Changed 5 years ago by yehuda

  • Milestone changed from 1.4 to 1.5

comment:13 Changed 4 years ago by snover

  • Status changed from reopened to open
  • Milestone changed from 1.4.4 to 1.5

comment:14 Changed 4 years ago by dmethvin

#7257 is a duplicate of this ticket.

comment:15 Changed 3 years ago by tomgrohl

I don't know if this has already been fixed for jQuery 1.6, but I've found a way to fix this using attrHooks.

I've wrote a attrHook for the checked attribute, which will set the checked if true or "checked" was passed, and then removing the attribute if false or "" was passed, intead of just changing the attributes value.

Something like:

//For setting as checked
$(elem).attr("checked", true);
$(elem).attr("checked", "checked");

//For setting as un-checked
$(elem).attr("checked", false);
$(elem).attr("checked", "");

I did find something that might affect the attrHook working in xhtml documents.

I had the same html page. One saved as index.html and the other as index.xhtml.

Calling document.documentElement.nodeName on the first one returns "HTML" as html nodes are uppercase.

Calling it on index.xhtml, as it is classed as an xml document, returns "html", obvously due to xml node names being case sensitive.

This is used in Sizzle.isXML and is only checking for "HTML" (unless there is a reason for this). I think this is the reason why this bug occurs.

Because of this, some jQuery methods are affected when they shouldn't be, so attrHooks might not get called if document.documentElement.nodeName returns "html"

I changed Sizzle.isXML to ( adding toUpperCase to the nodeName ):

Sizzle.isXML = function( elem ) {
	// documentElement is verified for cases where it doesn't yet exist
	// (such as loading iframes in IE - #4833) 
	var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;

	return documentElement ? documentElement.nodeName.toUpperCase() !== "HTML" : false;
};

If this check affects removing a checked attribute in an xhtml document, it must affect others.

I've done some tests ( Couldn't add them into jsFiddle as it doesn't have 1.6 )

Which can be found here:  http://bit.ly/fG44bd

Last edited 3 years ago by tomgrohl (previous) (diff)

comment:16 Changed 3 years ago by timmywil

Yes, this has been done in attrhooks for 1.6. All falsy values will remove Boolean attributes such as checked. I haven't looked into the XHTML issue, but I'll do some testing.

comment:17 Changed 3 years ago by tomgrohl

@timmywil

Haha. Thought it might have been. Nice one!

comment:18 Changed 3 years ago by dmethvin

  • Component changed from core to attributes
  • Milestone set to 1.6

$().prop() will land with the attr rewrite.

comment:19 Changed 3 years ago by timmywil

  • Owner set to timmywil
  • Status changed from open to assigned

comment:20 Changed 3 years ago by timmywil

  • Status changed from assigned to closed
  • Version changed from 1.3.2 to 1.5.2
  • Resolution set to fixed

comment:21 Changed 3 years ago by timmywil

This has been changed. Not all falsy values will remove the common Boolean attributes, just false.

Note: See TracTickets for help on using tickets.