jQueryのdata-*属性キャッシュ仕様と対策

jQueryでdata-*属性を扱う際にjQuery.data()メソッドもしくはjQuery.attr()メソッドを使うことになるでしょうが、jQueryで扱えるdata-*属性のオブジェクトは前提としてキャッシュ仕様であることを理解しておかないと嵌ることが多々あります。

取得だけなら問題は起こらない

<div id="user1" data-id="1" data-user="Jack" data-date-of-birth="2000-10-01">Jack</div>

これらのdata-*属性を取得したい場合、jQueryであれば次のように記述します。

//data()の場合
jQuery("#user1").data("id"); // => 1 (数字扱い)
jQuery("#user1").data("user"); // => "Jack"
jQuery("#user1").data("date-of-birth"); // => "2000-10-01"
jQuery("#user1").data("dateOfBirth"); // => "2000-10-01"
//attr()の場合
jQuery("#user1").attr("data-id"); // => "1"  (文字列扱い)
jQuery("#user1").attr("data-user"); // => "Jack"
jQuery("#user1").attr("data-date-of-birth"); // => "2000-10-01"

jQuery.data()はjQuery1.4.3から追加された機能ですが、上のdateOfBirthのようにキャメルケースでアクセスする場合は、jQuery1.6より対応しています。

存在していないdata-*属性を設定するだけなら問題は起こらない

<div id="user2" >Tom</div>

先ほどの取得と違いデータを設定する場合、jQueryであれば次のように記述します。

//data()の場合
jQuery("#user2").data("id","2");
jQuery("#user2").data("user","Tom");
jQuery("#user2").data("date-of-birth","2001-01-01");
jQuery("#user2").data("dateOfBirth","2001-01-01");
//attr()の場合
jQuery("#user2").attr("data-id","2");
jQuery("#user2").attr("data-user","Tom");
jQuery("#user2").attr("data-date-of-birth","2001-01-01");

これの実行後は、このHTMLは次のようにちゃんとデータが設定されることになります。

<div id="user2" data-id="2" data-user="Tom" data-date-of-birth="2001-01-01">Tom</div>

再設定のときにキャッシュ仕様による問題が起こる

すでに次のようにデータが設定されているとしましょう。

<div id="user1" data-id="1" data-user="Jack" data-date-of-birth="2000-10-01">Jack</div>

このJackのデータをjQuery.data()メソッドもしくはjQuery.attr()メソッドを使って再設定する場合に、たとえば、誕生日を"1999-10-01"に書き換えたいとします。次のように記述することになるでしょう。

//data()の場合
jQuery("#user1").data("date-of-birth","1999-10-01");
jQuery("#user1").data("dateOfBirth","1999-10-01");
//attr()の場合
jQuery("#user1").attr("data-date-of-birth","1999-10-01");

これで書き換えできたのですが、実はjQueryで扱うjQuery.attr()メソッドではjQueryオブジェクト上は書き換えできていません。再度、取得しても、取得できる結果は書き換え前のデータが返ってくるだけです。

//data()の場合
jQuery("#user1").data("date-of-birth"); // => "1999-10-01"
jQuery("#user1").data("dateOfBirth"); // => "1999-10-01"
//attr()の場合
jQuery("#user1").attr("data-date-of-birth"); // => "2000-10-01"

本来はすべての結果で書き換えた"1999-10-01"の誕生日のデータが返ってきて欲しいわけですが、jQuery.attr()メソッドの場合は、HTML上の属性値を書き換えるだけで、jQueryオブジェクトとしてのデータ名に対する値はキャッシュされたままで、jQuery.attr()で設定しなおしてもデータ上の値を再設定することはできません。

またjQuery.data()の場合は、jQueryオブジェクトとしてのデータ名に対する値は書き換えられるのですが、逆にjQueryのキャッシュ仕様が原因でHTML上には反映しない(HTML属性値は書き換えない)ため、例えばCSSでHTMLの属性値にアクセスするような場合などに属性値に応じたスタイルの変更が反映されない事態などが起こり得ます。このようにjQueryではキャッシュ仕様で、jQuery.data()メソッドを使うにしろ、jQuery.attr()メソッドを使うにしても嵌ることになる要因があるのです。

解決策はネイティブJSで再設定すること

以上ここまで説明してきた通り、data-*属性にjQuery.data()またはjQuery.attr()でアクセスする場合はキャッシュ仕様前提であることを理解しておく必要があります。そこで煩わしいキャッシュ仕様を回避して、データ属性の値とHTML上の属性値の両方を再設定し直したい場合にはネイティブJavaScriptを使う必要がでてきます。

ネイティブJavaScriptにも取得・設定が簡単にできるdatasetという便利なプロパティが用意されているのですが、目新しいプロパティのため、現状ではサポート状況に難があります。

IE
IE11よりサポート
Chrome
Chrome7よりサポート
Firefox
Firefox6よりサポート
Safari
Safari5.1よりサポート
Opera
Opera11.1よりサポート
iOS
iOS5.0よりサポート
Andoroid
Android 3.0よりサポート

ご覧の通り、Internet Explorerのサポートが11からになるため実質的に使えないという理由があります。そこでdatasetプロパティの代替策として、getAttribute()メソッド, setAttribute()メソッドを使うことになります。

<div id="user1" data-id="1" data-user="Jack" data-date-of-birth="2000-10-01">Jack</div>

すでにJackのデータを記述したHTMLがあるとして、Jackの誕生日を"1999-10-01"に書き換えます。

var user1 = document.getElementById("user1");
user1.setAttribute("data-date-of-birth","1999-10-01");
user1.dataset.dateOfBirth = "1999-10-01";

もしdatasetプロパティを使う場合は、jQuery.data()メソッド同様にキャメルケースで記述します。ただし、datasetプロパティのサポート状況に注意してください。

これだけで無事、データとHTML上の属性値の両方を再設定することができます。試しにjQuery.data()やjQuery.attr()で取得してもキャッシュ仕様による問題を回避できますし、ネイティブJavaScriptで取得してもすべてで期待通りの"1999-10-01"が返ってくることになります。

//data()の場合
jQuery("#user1").data("date-of-birth"); // => "1999-10-01"
jQuery("#user1").data("dateOfBirth"); // => "1999-10-01"
//attr()の場合
jQuery("#user1").attr("data-date-of-birth"); // => "1999-10-01"
//ネイティブJavaScriptの場合
var user1 = document.getElementById("user1");
user1.getAttribute("data-date-of-birth"); // => "1999-10-01"
user1.dataset.dateOfBirth; // => "1999-10-01"
Updated / Published