Skip to main content

Bug Tracker

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 dmethvin comment:1

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.

Changed November 21, 2010 01:01AM UTC by dmethvin comment:2

component: unfileddata
milestone: 1.51.4.5
priority: undecidedhigh
status: newopen

Changed December 03, 2010 04:31AM UTC by anonymous 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 dmethvin comment:4

owner: → dmethvin
status: openassigned

Changed December 09, 2010 02:51AM UTC by dmethvin comment:5

#7231 is a duplicate of this ticket.

Changed December 09, 2010 03:55AM UTC by dmethvin comment:6

Changed December 09, 2010 05:40PM UTC by john comment:7

resolution: → wontfix
status: assignedclosed

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 john comment:8

resolution: wontfix
status: closedreopened

Changed December 09, 2010 05:54PM UTC by john 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 dmethvin comment:10

keywords: → needsreview
milestone: 1.4.51.5

Changed December 09, 2010 11:14PM UTC by jitter comment:11

#7742 is a duplicate of this ticket.

Changed December 16, 2010 09:55AM UTC by armloch@gmail.com 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 paul.irish 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 jdub@bethesignal.org 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 dmethvin comment:15

keywords: needsreviewneedsdocs
resolution: → wontfix
status: reopenedclosed

Changed January 24, 2011 10:18PM UTC by dmethvin 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 dmethvin comment:17

keywords: needsdocs

Changed January 24, 2011 11:30PM UTC by jitter comment:18

#8042 is a duplicate of this ticket.

Changed January 24, 2011 11:42PM UTC by embrangler 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 jitter comment:20

#8356 is a duplicate of this ticket.

Changed March 03, 2011 08:58PM UTC by addyosmani comment:21

#8435 is a duplicate of this ticket.

Changed March 30, 2011 04:07AM UTC by ajpiano comment:22

#8709 is a duplicate of this ticket.

Changed March 30, 2011 02:53PM UTC by awensley@gmail.com 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 mattw@techsoftinc.com 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 jyrki@taiste.fi 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 rwaldron comment:26

#9688 is a duplicate of this ticket.

Changed August 31, 2011 07:56AM UTC by anonymous 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 anonymous 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 rwaldron 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 dmethvin 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 dmethvin comment:31

#10856 is a duplicate of this ticket.

Changed December 02, 2011 08:44PM UTC by jdmarshall 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 jursetto 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 rwaldron comment:34

Or...

data-foo="true"

$('#abc').attr('data-bar') -> "true"

... as shown above

Changed December 14, 2011 01:40AM UTC by jursetto 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 dmethvin comment:36

#11060 is a duplicate of this ticket.

Changed January 11, 2012 07:22PM UTC by anonymous 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 dmethvin 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 david@wolever.net 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 rwaldron comment:40

Use attr

Changed January 19, 2012 07:16PM UTC by david@wolever.net comment:41

Changed February 28, 2012 02:56AM UTC by draeton@gmail.com 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));

https://gist.github.com/1928836

Changed March 06, 2012 01:47PM UTC by dmethvin comment:43

#11445 is a duplicate of this ticket.

Changed March 24, 2012 06:25PM UTC by anonymous 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 rwaldron comment:45

_comment0: Can't be fixed with breaking core functionality, posting more comments won't change fact1332617795062923

Can't be fixed without breaking core functionality, posting more comments won't change fact

Changed June 02, 2012 12:25AM UTC by dvirazulay 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 dmethvin 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 gibson042 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 dmethvin comment:49

#12052 is a duplicate of this ticket.

Changed July 11, 2012 04:33PM UTC by rwaldron comment:50

#12060 is a duplicate of this ticket.

Changed July 25, 2012 02:33PM UTC by Dave Methvin comment:51

resolution: wontfixfixed

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 dmethvin comment:52

milestone: 1.51.8

Changed August 17, 2012 03:32PM UTC by timmywil comment:53

#12332 is a duplicate of this ticket.

Changed September 02, 2012 05:51AM UTC by latchkey 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 dmethvin 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 mikesherov comment:56

keywords: needsdocs