jQueryの$elm.data()で取得できる値は暗黙でstringから型変換される

知らなくてコケた

通常,element.datasetから得られるカスタムデータ属性の値はstringですが,jQueryのdataメソッドはこれらを暗黙的に型変換します.

以下コード踏まえたサンプル.

<!-- 長いので改行いれてます -->
<div id="test"
     data-str="hogefuga"
     data-num="01234"
     data-bool="true"
     data-ary="[1,2,3]"
     data-obj='{"foo":"bar"}'>

     <p>ほげほげほげ</p>
</div>

たとえば,このようなdata-*がコッテリ付いた要素があったとして,その要素から$elm.data()で値を取得すると・・・

var $elm = $('#test');

// string
$elm.data('str');  // 'hogefuga'

// number
$elm.data('num');  // 1234

// boolean
$elm.data('bool'); // true

// array
$elm.data('ary');  // [1,2,3]

// object
$elm.data('obj');  // {foo:'bar'}

なんと型変換されます.オブジェクトっぽい文字列はobjectに変換されますし,その他それっぽい文字列はそれっぽい型に変換されて渡されます.

自分がコケたのは,例にも挙げたようなnumericだけど先頭が0のstringを,numberに変換されて1234なってしまっていたパターン.仕様を把握してませんでした.lol

確実にstringのまま取得したいならattrを使う

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. To retrieve the value's attribute as a string without any attempt to convert it, use the attr() method..data() – jQuery API

dataメソッドはstringから色々型変換しちゃうから,ピュアなstringで取得したいならattr使うといいよ,という話でした.

ドキュメント読んでなかった自分が悪いのは棚に上げつつ,キモいすごく親切ですね.

追記おまけ

jQuery source viewerから変換部分の実装拾ってきた.うーん...直球(;´Д`)

// `dataAttr` from 1.7pre Live From Git (3ad0ba62f03ff3d7aec7d3dfb25492cdf33350a7)

function dataAttr(elem, key, data) {
    // If nothing was found internally, try to fetch any
    // data from the HTML5 data-* attribute
    if (data === undefined && elem.nodeType === 1) {

        var name = "data-" + key.replace(rmultiDash, "-$1").toLowerCase();

        data = elem.getAttribute(name);

        if (typeof data === "string") {
            try {
                data = data === "true" ? true : data === "false" ? false : data === "null" ? null : jQuery.isNumeric(data) ? parseFloat(data) : rbrace.test(data) ? jQuery.parseJSON(data) : data;
            } catch(e) {}

            // Make sure we set the data so it isn't changed later
            jQuery.data(elem, key, data);

        } else {
            data = undefined;
        }
    }

    return data;
}

個人的には,$elm.data(key[, type])ぐらいにして,typeの明示があったときだけ変換してくれればいいと思う.実際はsetとgetのインターフェース混ざってるからできなさそうだけど.