Side navigation
#7579 closed bug (fixed)
Opened November 19, 2010 10:58PM UTC
Closed January 24, 2011 10:16PM UTC
Last modified October 15, 2012 09:57PM UTC
jQuery.data() truncates numbers taken from data-xxx attributes
Reported by: | dmethvin | Owned by: | dmethvin |
---|---|---|---|
Priority: | high | Milestone: | 1.8 |
Component: | data | Version: | 1.4.4 |
Keywords: | Cc: | ||
Blocked by: | Blocking: |
Description
Test case: http://jsfiddle.net/dmethvin/8SsWK/
Discussion: http://forum.jquery.com/topic/data-type-conversion
I found an issue with using .data() to access data-* attributes and how it does automatic type conversion. The conversion will break any string that just happens to look like a JavaScript value. A simple example:
HTML:
<div id="foo" data-version="1.0">...</div>
JS:
jsversion = $("#foo").data("version");
jsversion is now the number 1. That is totally wrong if you intend to use that attribute as a string. (I was using it to create a URL where "1.0" is the valid value.)
You can get around this with direct access: $("#foo").attr("data-version") but that's not as much fun. Also, the process is not reversible which is confusing:
$("#foo").data("foo", "1.0");
val = $("#foo").data("foo");
val is the string "1.0" and not converted to number 1.
passing in 1.0 vs "1.0" would of course result in 1.
The data() docs say "Note that strings are left intact while JavaScript values are converted to their associated value (this includes booleans, numbers, objects, arrays, and null)". That seems poorly worded since "1.0" is a string just as much as "true", "false", or "null". It should say every attempt is made to convert the string to a JavaScript value otherwise it is left as a string. It would be nice to also mention that if you need literal strings you must use the attr() method mentioned above.
I'd suggest the automatic conversion be removed and let users deal with it as needed. Perhaps add a helper function or alternate data() function to do conversion. The way it works now is intentional and has test cases but I'm unsure what the motivation of this conversion is in the first place? Would it break a common use case to just always treat attributes as strings?
Attachments (0)
Change History (56)
Changed November 20, 2010 06:29PM UTC by comment:1
Changed November 21, 2010 01:01AM UTC by comment:2
component: | unfiled → data |
---|---|
milestone: | 1.5 → 1.4.5 |
priority: | undecided → high |
status: | new → open |
Changed December 03, 2010 04:31AM UTC by comment:3
If one would implement a getter for the "data-" attribute the way W3C proposes, the getter would have to return a string.
Quoting the W3C HTML5 Working Draft:
«Except where otherwise specified, attributes on HTML elements may have any string value, including the empty string. Except where explicitly stated, there is no restriction on what text can be specified in such attributes.»
http://www.w3.org/TR/html5/elements.html#custom-data-attribute
Changed December 09, 2010 02:48AM UTC by comment:4
owner: | → dmethvin |
---|---|
status: | open → assigned |
Changed December 09, 2010 03:55AM UTC by comment:6
Pull request: https://github.com/jquery/jquery/pull/124
Changed December 09, 2010 05:40PM UTC by comment:7
resolution: | → wontfix |
---|---|
status: | assigned → closed |
Uhhh... what? This is a major regression! As mentioned in other tickets - if you want the actual precise value just do: .attr("data-version").
Changed December 09, 2010 05:53PM UTC by comment:8
resolution: | wontfix |
---|---|
status: | closed → reopened |
Changed December 09, 2010 05:54PM UTC by comment:9
So, I guess we should definitely discuss this more because 1.5 is a time during which we could change thing (although it'll definitely break code).
Changed December 09, 2010 06:38PM UTC by comment:10
keywords: | → needsreview |
---|---|
milestone: | 1.4.5 → 1.5 |
Changed December 09, 2010 11:14PM UTC by comment:11
#7742 is a duplicate of this ticket.
Changed December 16, 2010 09:55AM UTC by comment:12
big problem for me. my "numbers" are too large and parseInt() doesn't parse them correctly:
var foo = "1283767437056205165"
parseInt(foo)
1283767437056205000
-
simon
Changed December 20, 2010 12:37AM UTC by comment:13
I have updated the docs for $.fn.data to Dave's suggested text. http://api.jquery.com/data/
It now reads:
Every attempt is made to convert the string to a JavaScript value (this includes booleans, numbers, objects, arrays, and null) otherwise it is left as a string. If you need literal strings you must use the attr() method.
Changed January 03, 2011 02:29PM UTC by comment:14
Found a related problem. Here's the HTML as rendered (not set up by jQuery):
<a class="screen-name username" data-screen-name="0xDECAFB4D" href="https://twitter.com/0xDECAFB4D" title="mls" target="_blank">0xDECAFB4D</a>
Then at the console:
$('a[title=mls]').data('screen-name') 0 $('a[title=mls]').attr('data-screen-name') "0xDECAFB4D"
Thanks. The suggestions to only add smarts to the [ and { cases sound pretty sensible to me. :-)
Changed January 24, 2011 10:16PM UTC by comment:15
keywords: | needsreview → needsdocs |
---|---|
resolution: | → wontfix |
status: | reopened → closed |
Changed January 24, 2011 10:18PM UTC by comment:16
If you want the data attributes as entered in the HTML, use .attr("data-whatever") instead.
Changed January 24, 2011 10:36PM UTC by comment:17
keywords: | needsdocs |
---|
Changed January 24, 2011 11:30PM UTC by comment:18
#8042 is a duplicate of this ticket.
Changed January 24, 2011 11:42PM UTC by comment:19
_comment0: | Ugh. I disagree with wontfix-ing this. \ \ It's ok if .data() doesn't work for large ints, but it shouldn't pretend to be working and return wrong values. \ \ This text mentioned in comment 13 is misleading: \ {{{ \ Every attempt is made to convert the string to a JavaScript value (this includes booleans, numbers, objects, arrays, and null) otherwise it is left as a string. \ }}} \ For large ints the result is a WRONG integer value. I would expect it to be a string, since it fails to convert properly. → 1295912617452825 |
---|
Ugh. I disagree with wontfix-ing this.
It's ok if .data() doesn't work for large ints, but it shouldn't pretend to be working and return wrong values.
This text mentioned in comment 13 is misleading:
Every attempt is made to convert the string to a JavaScript value (this includes booleans, numbers, objects, arrays, and null) otherwise it is left as a string.
For large ints the result is a WRONG integer value. I would expect it to be a string, since it fails to convert properly.
Changed February 22, 2011 10:56PM UTC by comment:20
#8356 is a duplicate of this ticket.
Changed March 03, 2011 08:58PM UTC by comment:21
#8435 is a duplicate of this ticket.
Changed March 30, 2011 04:07AM UTC by comment:22
#8709 is a duplicate of this ticket.
Changed March 30, 2011 02:53PM UTC by comment:23
I also think not fixing this is a mistake. What use is .data() if you can't trust the data it returns?
I guess I just can't use .data() for anything anymore then because I never know what values might trigger this bug. Bummer.
Changed March 30, 2011 04:42PM UTC by comment:24
This would be less annoying if we could quote the string-that-looks-like-a-JS-value and have that work. But I just tried that, and it leaves the quotes in.
Using .attr("data-xxx") is problematic if the intent was to use the attribute to initialize the value at page load, then use .data to update the value at a later time. Now you no longer have a unified way to access the current value.
I guess I'll prefix and substring as my workaround.
Changed April 08, 2011 09:03AM UTC by comment:25
The implicit conversion of .data causes too much problems. A few example:
"01234" -> 1234 "74e2" -> 7400
We're constantly having problems with this and now we are resorting in prefixing it. We can't use attr as we generate data attributes in mustache templates and modify them later on with .data. The data is random "hexadecimal" strings, so that's why we're running in severe problems with this.
This ticket should be reopened and the implicit behavior of the data function reconsidered
Changed June 28, 2011 08:10PM UTC by comment:26
#9688 is a duplicate of this ticket.
Changed August 31, 2011 07:56AM UTC by comment:27
Automatic type conversion is a really bad idea and should be removed from jQuery.
I was storing some IDs as data attributes. IDs are strings not numbers - although they look like numbers. When I access them with the data() function they are cast as numbers and become corrupted because integers in JavaScript are only considered accurate up to 15 digits.
jQuery shouldn't be treating developers as idiots and trying to take control of their programs. If you want to have type conversion, have it as an option, but it should not be the default behaviour.
Changed November 01, 2011 08:13PM UTC by comment:28
ok, this is completely massed up, why on earth would one do this? Is jQuery catering to kids? Stupid thing was converting my product codes with leading zeros to integers... so 00800 ended up 800... you should leave conversion up to developers...
had to change
var prodCode = $(itemBlock).data("prodcode");
to
var prodCode = $(itemBlock).attr("data-prodcode");
Changed November 01, 2011 08:38PM UTC by comment:29
Unfortunately, before jQuery.fn.data() was open to the public, it was a core-use-only function that had nothing to do with HTML5 data-attrs (predates them), and as such, it stored only real JavaScript values - again, nothing to do with HTML5 data-attrs. Then one day it was given to the public... then soon after the public wanted it to interface with data-attrs, so jQuery allowed it, but the function still had to support all of the extant code using the original jQuery.fn.data() behaviour.
This explanation could've been derived by reading the above thread, so next time, try to be a little more understanding and a little less aggressive.
Changed November 01, 2011 11:00PM UTC by comment:30
I think we've explained the use of the .data()
method pretty clearly here, I hope it helps:
http://www.learningjquery.com./2011/09/using-jquerys-data-apis
TL;DR: If you simply want a string, use .attr()
-- data attributes *are* attributes after all
Changed November 22, 2011 01:51PM UTC by comment:31
#10856 is a duplicate of this ticket.
Changed December 02, 2011 08:44PM UTC by comment:32
The thread is basically apologizing for not following the spec, and for losing data precision. Marking it "won't fix" is bound to earn you "aggressive" feedback. I understand, now, how it got to be that way, and that's very interesting. However, understanding is not the same as saying "It's okay". This is definitely NOT okay.
There has to be a way to share code without making both bits (mis)function identically.
Changed December 14, 2011 12:07AM UTC by comment:33
One trick to make sure your data isn't converted is to pass a quoted string inside a 1-element array, then reference that element. This will ensure the JSON parser is engaged because it starts with [. It also lets you pass arbitrary objects you do want converted (in which case you can't use
.attr()).
Example:
<input id="abc" data-foo='["true"]' data-bar='[true]'> $('#abc').data('foo')[0] -> "true" $('#abc').data('bar')[0] -> true
Changed December 14, 2011 12:14AM UTC by comment:34
Or...
data-foo="true"
$('#abc').attr('data-bar') -> "true"
... as shown above
Changed December 14, 2011 01:40AM UTC by comment:35
That's right, but you skipped the second part of my comment; with attr() you can't accept either a boolean value OR a string, whereas you can with the technique above. Ticket #7231 had some people asking for the ability to do both, although it looks like this ticket didn't.
Changed January 11, 2012 04:21PM UTC by comment:36
#11060 is a duplicate of this ticket.
Changed January 11, 2012 07:22PM UTC by comment:37
Wow, this is so beyond absurd.
Basically, you have a whole group of USERS who are asking you to fix this, with valid reasons, and you are basically ignoring them, saying to just use another method. Even though there is a perfectly acceptable, backwards compatible solution to this problem.
I can't even begin to understand the mentality behind this type of decision. dmethvin, are you just hard headed?
Changed January 11, 2012 07:32PM UTC by comment:38
Yes, I am hard headed if I see that changing documented interfaces will cause a lot of trouble. If I had a time machine I could go back and redefine this interface to be more compatible with the HTML5 spec that was not yet written, but I do not.
You feel passionately about changing this because you have no existing code that would break. Imagine how mad you would be if you had deployed code based on the current behavior and we changed it.
To use your pent-up energy, try writing a patch that makes it behave the way you ideally would like it to, but does not break old code. Or, just use .attr()
which works fine.
Changed January 19, 2012 08:34AM UTC by comment:39
Would a patch be considered which allows quoted strings? For example, so that: … data-foo='"123"' …
could be used to force a string?
For example, changing the existing logic:
data = data === "true" ? true : data === "false" ? false : data === "null" ? null : jQuery.isNumeric( data ) ? parseFloat( data ) : rbrace.test( data ) ? jQuery.parseJSON( data ) : data;
To move the rbrace.test
call above isNumeric
, and modifying it to include "
:
var jsonDataTest = /^(?:\\{.*\\}|\\[.*\\]|".*")$/ ... data = data === "true" ? true : data === "false" ? false : data === "null" ? null : jsonDataTest.test( data ) ? jQuery.parseJSON( data ) : jQuery.isNumeric( data ) ? parseFloat( data ) : data;
This would guarantee (I believe) that any JSON value in a data-
property would be correctly encoded, and it would provide a safe (and probably backwards compatible?) mechanism for storing strings-which-look-like-numbers.
Changed January 19, 2012 02:16PM UTC by comment:40
Use attr
Changed January 19, 2012 07:16PM UTC by comment:41
Changed February 28, 2012 02:56AM UTC by comment:42
Considering the lack of will to patch jQuery, here's a simple plugin:
(function ($) { "use strict"; var dataTest = /^data-/; var dashesToCamelCase = function (name) { return name.replace(dataTest, "").replace(/-\\w/g, function (str) { return str.slice(1).toUpperCase(); }); }; $.fn.getData = function () { var data = {}; var el = this.get(0); var attr = el.attributes; var name; var i, l; for (i = 0, l = attr.length; i < l; i++) { if (dataTest.test(attr[i].name)) { name = dashesToCamelCase(attr[i].name); data[name] = attr[i].nodeValue; } } return data; }; }(jQuery));
Changed March 06, 2012 01:47PM UTC by comment:43
#11445 is a duplicate of this ticket.
Changed March 24, 2012 06:25PM UTC by comment:44
Better to fix this now than to have loads of more people continue to implement this poor solution. I'm sure more people are running into problems with their code right now than than would be affected by the change because they are counting on this automatic type conversion. Worse yet, many don't know about the problem till it hits them some time in the future when they least expect it.
Changed March 24, 2012 07:33PM UTC by comment:45
_comment0: | Can't be fixed with breaking core functionality, posting more comments won't change fact → 1332617795062923 |
---|
Can't be fixed without breaking core functionality, posting more comments won't change fact
Changed June 02, 2012 12:25AM UTC by comment:46
Are you serious?
How about not supporting those users that used .data, thinking it will be treated as a string (like it should have been) and suddenly when their numbers grow over a certain point, or one decides to enter a number that will be truncated/rounded due to this and gets the wrong behavior out of it?
This will be a real mess to debug.
If no one can come up with a real reason why this might break old code, why not implement it? There was enough time to think of a valid reason not to fix this huge flaw, which makes .data obsolete and not reliable.
Changed June 02, 2012 12:29AM UTC by comment:47
@dvirazulay, it would never break old code because the .data()
API existed before HTML5 defined data-
att ... aw, just shut up and read this article.
http://www.learningjquery.com/2011/09/using-jquerys-data-apis
Changed June 03, 2012 04:56AM UTC by comment:48
@dvirazulay, feel free to plugin your desired behavior with a gist: https://gist.github.com/2861953. Same goes for those passionate about #11297 and #10174.
Changed July 10, 2012 08:59PM UTC by comment:49
#12052 is a duplicate of this ticket.
Changed July 11, 2012 04:33PM UTC by comment:50
#12060 is a duplicate of this ticket.
Changed July 25, 2012 02:33PM UTC by comment:51
resolution: | wontfix → fixed |
---|
Fix #7579. Don't convert to number if it changes the string. Close gh-852.
Net effect here is that hex numbers and most exponential-format numbers or long sequences of digits will remain strings rather than being coerced to numbers. `The people have spoken.
Changeset: ce15bd7d0c14106521bd21179b1507f2863d1960
Changed July 25, 2012 02:36PM UTC by comment:52
milestone: | 1.5 → 1.8 |
---|
Changed August 17, 2012 03:32PM UTC by comment:53
#12332 is a duplicate of this ticket.
Changed September 02, 2012 05:51AM UTC by comment:54
I've been thinking a lot about this for the last couple of days and have come to the conclusion that this fix is a terrible idea that only creates even more confusion in the long term. It really is a hack on top of a bad design decision in the first place. Now, the message is 'use .data() instead of .attr() because it is kinda safe to use in certain circumstances.'
If you generate a div such as <div data-foo="4.000"></div>, you get back a String... $('div').data('foo') despite the fact that the documentation says it tries to coerce numbers. If I do $('div').data('foo', 4.000) and then look at it with .data('foo'), it is a number. If I do data-foo="4000", it is a number. I'm sorry, but this is very inconsistent behavior.
Changed September 02, 2012 01:49PM UTC by comment:55
keywords: | → needsdocs |
---|
Proposed addition to the docs:
A value is only converted to a number if doing so doesn't change the value's representation. For example, "1E02" and "100" are equivalent as numbers but not as strings, so "1E04" remains a string. A value like "1.4000" is not converted to number because the trailing zeroes would be stripped.
Changed October 15, 2012 09:57PM UTC by comment:56
keywords: | needsdocs |
---|
I've updated the test case with a few more examples. Based on these I think we'd be better to back off the magic for all but {} and [], returning everything else as string.