みかづきブログ・カスタム

基本的にはちょちょいのほいです。

IE11でdatasetをつかってdata属性を動的に更新すると属性セレクタで指定したスタイルが反映されない 💻

IE11


結論

  • 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を試すべきだと。

しかし、ソースを読むまでは全く気づきませんでした。困ったらソースを読んでみることは大切ですね。