結論
- IE11ではdatasetでdata属性を更新してもスタイルが反映されない
- IE11でもsetAttributeでdata属性を更新すればスタイルが反映される!
詳細
かれこれ、ずーっと悩んでいたことだったのですが、IE11では、
- 属性セレクタをつかってスタイルを指定
- datasetをつかってdata属性を更新
を組み合わせて使うと、スタイルが更新されません。
例えば下記のようなケースです。
HTML
<div data-color="red"></div>
CSS
[data-color="red"] { background-color: red; } [data-color="blue"] { background-color: blue; } [data-color="green"] { background-color: green; }
JavaScript
const colors = ['red', 'blue', 'green']; let colorIndex = 0; setInterval(() => { colorIndex = (colorIndex + 1) % colorIndex.length; document.querySelector('[data-color]').dataset.color = colors[colorIndex]; }, 1000);
上記コードは分かりやすさ重視で、必要な箇所のみ記載していますが、実際に試すときはdivに大きさを指定したり、IE11で動くようにアローファンクションをやめたりする必要があります。
それらを施したデモがこちらです。
DEMO
develop.kimizuka.org
1秒ごとに背景色が変更されることを期待していますが、IE11で試すと、datasetを更新しているdiv(左端)は初期値から変更されません。Chromeでは意図通りの挙動になります。
ディベロッパーツールで確認すると、データ属性は更新されているので、再描画が発生していないことが原因かと思われます。
なので、これまでは、クラス属性を振ったり、意味なしのz-indexを更新したりして強制的に再描画を発生させることで解決を図ってました。
しかし、ReactやVueではこの現象が発生しません。動的にdata属性を更新してもしっかり再描画されていることがずーっと疑問で、本日思い切ってVueのソースコードを読んでみることにしました。
該当箇所を抜粋してみると、
function baseSetAttr (el, key, value) { if (isFalsyAttrValue(value)) { el.removeAttribute(key) } else { // #7138: IE10 & 11 fires input event when setting placeholder on // <textarea>... block the first input event and remove the blocker // immediately. /* istanbul ignore if */ if ( isIE && !isIE9 && el.tagName === 'TEXTAREA' && key === 'placeholder' && value !== '' && !el.__ieph ) { const blocker = e => { e.stopImmediatePropagation() el.removeEventListener('input', blocker) } el.addEventListener('input', blocker) // $flow-disable-line el.__ieph = true /* IE placeholder patched */ } el.setAttribute(key, value) } }
と、いろいろ書いてありますが、今回のケースではif文の条件がtrueにならないので、最後の el.setAttribute(key, value) が実行されるだけです。
あれ、普通にsetAttributeでセットしているだけだ。なんで再描画がされるんだろう。と思っていたのですが。
気づきました。
datasetではなく、setAttributeを使って更新すれば良いのではなかろうかと。
で、実際に試すと意図通りの挙動になりました。(右から2番目)
develop.kimizuka.org
気づいてみれば確かにその手があったかと思います。datasetでダメならsetAttributeを試すべきだと。
しかし、ソースを読むまでは全く気づきませんでした。困ったらソースを読んでみることは大切ですね。