HTML要素の中身の変化を監視して、変化があった場合に元の内容に戻すよう書き換える方法を忘備録として残しておきます。
仕掛けとしては、
1. HTML要素を生成
2. MutationObserverにより1で作った要素を監視
3. 何かしらの原因で1に変更があった場合、2の生成時に設定したコールバックが呼び出される。
4. 1の時点の要素になるよう中身を書き換える
という流れです。
デモコードを使って詳しくその仕組みについて見ていきます。
デモでは要素の属性を監視し、その属性に変更が加えられたときに元に戻す流れを示していきます。
7行目に自分が用意したHTMLがあるとします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> </head> <body> <h1 id="hello" style="color: red;">Hello</h1> <script> document.getElementById("hello").style = "color: green;"; </script> </body> </html> |
しかし10行目で何かしらの要因によりstyle属性が書き換えられてしまいました。
そこでstyle属性に変更を監視するMutationObserverに元の中身を復元するようにHTMLを書き直すコールバックを設定することで、あたかも変更が無かったかのようになります。11行目のコードがないと、MutationObserverに設定したコールバックの中身の12行目のコードの実行が再びMutationObserverに設定したコールバックを呼び出してしまうので無限ループになってしまいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> </head> <body> <h1 id="hello" style="color: red;">Hello</h1> <script> let observer = new MutationObserver(() => { observer.disconnect(); document.getElementById("hello").style = "color: red;"; }); observer.observe(document.getElementById("hello"), { attributes: true, attributeFilter: ["style"] }); </script> <script> document.getElementById("hello").style = "color: green;"; </script> </body> </html> |
ただしこれだと不十分な場合があります。
変更が1回だけならこれでよいのですが、複数回の変更 (21、25行目) が予期される場合は11行目のコードがネックになります。これは最初の変更が検知されたときに呼び出されるコールバックで監視を停止するからです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> </head> <body> <h1 id="hello" style="color: red;">Hello</h1> <script> let observer = new MutationObserver(() => { observer.disconnect(); document.getElementById("hello").style = "color: red;"; }); observer.observe(document.getElementById("hello"), { attributes: true, attributeFilter: ["style"] }); </script> <script> document.getElementById("hello").style = "color: green;"; </script> <script> document.getElementById("hello").style = "color: green;"; </script> </body> </html> |
この問題はコードを以下のように修正することで対処することができます。つまり、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> </head> <body> <h1 id="hello" style="color: red;">Hello</h1> <script> let observer = new MutationObserver(() => { if(document.getElementById("hello").getAttribute("style") !== "color: red;"){ document.getElementById("hello").style = "color: red;"; } }); observer.observe(document.getElementById("hello"), { attributes: true, attributeFilter: ["style"] }); </script> <script> document.getElementById("hello").style = "color: green;"; </script> <script> document.getElementById("hello").style = "color: green;"; </script> </body> </html> |
というようにstyle属性が所望の形以外に変化したときのみstyle属性を変更させるコールバックを設定することで複数回のstyle属性の変化にも対応できます。
以上変更を監視するMutationObserverを使ってHTML要素に加えられた変更を元に戻す方法でした。
用途としては、ライブラリで管理されている要素の中身をカスタマイズしたり、
Stylusなどでcssの書き換えを反映させないようにしたりすることなどがあります。