39

Why is the value of data-value="2.0" cast to a String and the value of data-value="2.5" cast to a Number? I can handle this fine within my function. I'm just trying to understand a little more about how Javascript handles Numbers and Strings. This kind of caught me off guard.

<a data-value="2.0">2.0</a>
<a data-value="2.5">2.5</a>
$("a").click(function() {
    alert(typeof $(this).data( "value"));   
});

[ Fiddle With It ]

Scimonster
  • 32,893
  • 9
  • 77
  • 89
Matt
  • 549
  • 4
  • 11
  • I don't know WHY this is happening but you can see that floats ending with a `0` are always pretended to be strings. Maybe because the last `0` is irrelevant and so interpreted as text. For example: `2.5` is number and `2.50` is string. – TheFrozenOne Sep 30 '14 at 16:52
  • 17
    jQuery's `.data` method will attempt to convert whatever is in the `data-*` attribute - if you want it as the raw string value, use `.attr("data-*")` – tymeJV Sep 30 '14 at 16:53
  • fyi, I'm using jquery 1.7.1 in a project and it's returning "6" when the data attribute is "6.00". – But those new buttons though.. Mar 09 '18 at 02:49

6 Answers6

31

Those values are simply strings to vanilla javascript; it's not attempting any conversion on its own.

console.table(
  [...document.querySelectorAll("a")]
    .map(({dataset:{value}}) => ({
      type: typeof value,
      value
    }))
);
<a data-value="2.0">2.0</a>                                                       <script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script><script>console.config({maximize:true,timeStamps:false})</script><style>.as-console-wrapper{display:block;}</style>
<a data-value="2.5">2.5</a>

jQuery, on the other hand, tries to determine and convert to the appropriate type when accessing data attributes via data(). It's (arguably) an issue with their implementation. Their documentation (emphasis mine) actually addresses this:

Every attempt is made to convert the string to a JavaScript value (this includes booleans, numbers, objects, arrays, and null). A value is only converted to a number if doing so doesn't change the value's representation. For example, "1E02" and "100.000" are equivalent as numbers (numeric value 100) but converting them would alter their representation so they are left as strings. The string value "100" is converted to the number 100.

See also HTMLElement.dataset

canon
  • 40,609
  • 10
  • 73
  • 97
  • 2
    Thank you! I did not realise JQuery did this. I spent hours combining through a 600 line script looking for a bug and now feel very foolish! +1 For the example. – Matt Sep 30 '14 at 18:22
  • 1
    Good to know about this change. Turns out it's been this way since 1.8 rc 1. – zzzzBov Oct 01 '14 at 04:15
  • Actually dataset seems to convert everything to string EVEN IF assign through JavaScript. This caused me issues when trying to store a false boolean that would end up being evaluated as a "true" string which equals true since it's not empty. – Virus721 Jun 27 '22 at 14:53
  • @Virus721 Yes, the documentation for `dataset` (linked in my answer) explicitly says, _"When the attribute is set, its value is always converted to a string."_ (https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset#setting_values) – canon Jun 29 '22 at 18:57
23

Looking through jQuery's source code, i have identified the reason.

It will parse it as a number, if, and only if, doing so does not change the string. Source: https://github.com/jquery/jquery/blob/master/src/data.js#L36

(+"2.0") + "" === "2" // !== the original string
(+"2.1") + "" === "2.1" // == the original string
Scimonster
  • 32,893
  • 9
  • 77
  • 89
4

By default anything parsed from an attribute will be a string, you will need to convert it to a number if you need to use it as a number. The best way I've been able to handle this is by wrapping it with the Number function (native javascript)

var number = Number($(this).data("value"));
parseInt will return an integer (whole number), so if you want to keep your decimal values you'll need to use Number. Word of caution with parseInt, it is always better to specify the base if you want to parse an int, parseInt($(this).data("value"), 10);
zmehboob
  • 99
  • 3
  • Why would he want to `parseInt()` at all? He has decimal values like `2.5`. – canon Oct 02 '14 at 16:48
  • I had posted my answer after @dave lunny, who had referenced parseInt earlier but has since edited it. I should have been more explicit. I'll strike through that portion to avoid confusion. – zmehboob Oct 02 '14 at 17:16
1

It appears to be treating that ".0" extension of the number like a string.

If all of your data values are going to be coming in in that format, just whip a parseFloat() on those suckers like this:

$("a").click(function() {
    alert(typeof parseFloat($(this).data( "value")));   
});

That way it won't matter if they are strings or numbers, they will always be treated like numbers(decimals intact).

canon
  • 40,609
  • 10
  • 73
  • 97
Dave Lunny
  • 750
  • 8
  • 21
  • 2
    Wouldn't [`parseFloat()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat) be more appropriate given the `2.5`? – canon Sep 30 '14 at 17:04
1

As tymeJV mentioned, this looks like an issue with how jquery handles autoconversion. If you use "2", it gives a number as well, so I'm guessing its just a weird edge case in how they handle things. I would encourage just using .attr('xxx') and parsing out your data to its known type.

Brent Echols
  • 590
  • 2
  • 9
0

I think it's more a question about how jquery identifies numbers. If you use alert(typeof(this.getAttribute("data-value")));, it spits out that it's a string both times (which is expected). So far as I know, everything in an html attribute is considered a string, just different libraries may have default behavior that interprets them differently.

Also, a shorthand way to get a number would be something along the lines of...

var someNumber = +$(someElt).data("value");

the + will cause type coercion if the returned value is a string, or leave it alone if it's already a number.

Ixonal
  • 616
  • 8
  • 18