JavaScriptでHTMLの要素にデータを紐付けたいときは、WeakMapを使うとよい。
WeakMapとは
WeakMapとは、オブジェクトをキーとして、それに値を紐付ける仕組みである。詳しく知りたい人は、uhyohyo.netの記事を読むとよいだろう。
なお、WeakMapはIE11でもサポートされている。
例
<button type="button" class="button">ボタンA</button>
<button type="button" class="button">ボタンB</button>
<button type="button" class="button">ボタンC</button>
<button type="button" class="resetButton">リセット</button>
const map = new WeakMap();
const buttons = document.querySelectorAll('.button');
const resetButton = document.querySelector('.resetButton');
buttons.forEach((el) => {
const dataset = { count: 0 };
map.set(el, dataset);
el.addEventListener('click', () => {
dataset.count += 1;
console.log(`${el.textContent}: ${dataset.count}`);
});
});
resetButton.addEventListener('click', () => {
buttons.forEach((el) => {
const dataset = map.get(el);
dataset.count = 0;
});
});
☞ デモ
カスタムdata属性では駄目なのか
カスタムdata属性を使ってHTMLの要素にデータを保存することもできるが、それには2つ問題がある。
文字列しか保存できない
HTMLのすべての属性値は文字列なので、カスタムdata属性にも文字列しか保存できない。文字列ではない値をセットしても、文字列として保存される。
<div id="sample"></div>
const sample = document.getElementById('sample');
sample.dataset.number = 1;
console.log(sample.dataset.number === 1); // => false
console.log(sample.dataset.number === '1'); // => true
sample.dataset.boolean = true;
console.log(sample.dataset.boolean === true); // => false
console.log(sample.dataset.boolean === 'true'); // => true
グローバル空間である
そしてもっと大きな問題は、データの保存先がグローバル空間であるという点である。
カスタムdata属性にデータを保存するということは、プログラム全体で共有された空間に値を保存するということである。だからどこかで上書きされる危険性があるし、名前(属性名)の衝突という問題も起こりうる。
つまり、カスタムdata属性にデータを保存するということは、グローバル変数にデータを保存するようなものなのだ。
カスタムdata属性は、読み取り専用として使うのが安全だろう。
jQueryの.data()
では駄目なのか
jQueryの.data()
を使うと、要素に文字列以外のデータも保存できる。しかしデータの保存先がグローバル空間であるという点は、カスタムdata属性にデータを保存するのと同じである。