「MutationObserver」とは
ページの閲覧者側で動作するJavaScript(ユーザースクリプト)の実用的プログラムでは、多くの場合に「MutationObserver」が必要になります。
この機能は、ページ上の特定要素の状態を監視して、その変化を感知したら指定した関数を実行させる事に使います。 現代のウェブページは、表示の一部が変化する構成のページが多く、その度にページ上に新しい要素が出現します。 この新しい要素に対して JavaScriptは機能できず、いわゆる「発火しない」状態になります。 クリックすれば動作するはずのコードが、そこで終わってしまうのです。
この様な場合、新しい要素を取得しなおす事が必要です。 そのためには「新要素の出現」を監視して、出現をきっかけに動作する機能が必要です。 そういった事を可能にするのが「MutationObserver」で、この機能には「変異を監視する」といった名前が付けられています。
詳しくは以下のページが良く纏まって判り易いです。
過剰反応による自家中毒
上記ページの最後に、この機能の前身で廃止になった「MutationEvent」の事が書かれていますが、「MutationObserver」を使う上でも同様の注意が要ります。
例えば「body」の子要素までを含めて監視対象要素にして、変化があればプログラム全体を再起動させる設定をした時、プログラムの中に表示要素を生成させる下りがあると、その生成に「MutationObserver」が反応し、もう一度処理を再起動し… と繰り返す事になります。 これは無限に続いてページはフリーズしてしまいます。
この様な状態に陥らなくても、想定した以上に「MutationObserver」が反応して、動作はするが妙に遅い、ぎこちない、といった状態になる事があります。
「MutationObserver」が不可欠の場合は多いのですが、同時にそのコントロールが重要になる事が多いのは事実です。
必要に駆られて
現在、前ページの「Bad Iine Mute」の動作を検証中ですが、このツールでは7個の「MutationObserver」が機能しています。 特に複雑なのが、「管理トップ」「いいね!履歴」の実質3画面での動作の切換えで、「MutationObserver」の設定が悪いとツールが起動しなかったり、反対にページ読込みを遅延させたりと、落ち着きどころを探すのに苦労しています。
この「MutationObserver」の設定を最適化するため、「どのくらい機能しているか」を調べる事にしました。 目的とした動作をしているのか、過剰な動作をしていないかを、目視で判断しようという試みです。
先ず簡単に、ページの「body」の色を一瞬だけ「ブルー」に変化させる関数をコード上に作ってみました。 下がその例です。
設定では、1/100 secだけの表示ですが、充分の様です。 この関数を、調査する「MutationObserver」が起動させる関数の最初に仕込みます。
下は実際の周辺コードです。
最初の3行が「MutationObserver」の設定で、ここでは「mode_select」という関数を「head」(ページのヘッダー)の変化に反応して再実行する設定にしています。
「mode_select」の再実行は、普通は画面上で判りません。 しかし「call()」をその先頭で実行する様にしたので、再実行ごとにページの背景色が青色になり、目視でどの程度「MutationObserver」が動作したが判ります。
下は、「管理トップ」のリロード後に、「Bad Iine Mute」の「mode_select()」がこの「MutationObserver」で再起動される様子です。
❶「いいね!された記事」のタイトル部が表示される。
タイトル部が表示された時には、「Bad Iine Mute」のアイコンがインポーズされているので、ここの青背景は「mode_select()」が最初に起動された事が判る。 しかし、青背景の時間が長めで、複数回起動されている可能性がある。
❷ 実際の「いいね」データのローディングマークが僅かの時間表示される。
これは、「いいね」のデータをシステムから受け取っている状態。
❸「いいね」の記事リストとアイコン群が表示され、この瞬間に一瞬青背景になる。
これは、アイコン等の表示に「MutationObserver」が反応して「mode_select()」が再起動されたと考えられる。
動作チェックコードを改善
上のテストで、❶の時間が長いので、もしかしたらこの段階で複数回の実行があるのかと気になり、カウントコードを作りました。
ページを開くごとにカウントはリセットされますが、「call()」を忍ばせた関数が、再起動されられる回数を画面の左上隅に累積して表示します。
下は、「管理トップ」ページのリロードの数秒後(上の動画と同じ)の結果で、再起動の回数は「2」回だと判りました。
ここで、「青背景」のリセットコードを「background='inherit'」から「'none'」に変更すると、❶ の青背景の時間の遅延がなくなりました。 JavaScriptからの指定は、ページの本来の指定より強力で、「inherit」はリセットにタイムラグが生じる場合がある様です。
ここからが本当の作業
このページのテストコードを使い、「Bad Iine Mute」をもう少し煮詰め様と考えています。「Bad Iine Mute」は、合計 6個の画面で、「いいねブロック」のとても似たコードを動作させています。 しかし、それぞれの画面ごとに「いいね」リストのHtml構成が微妙に異なり、本来なら同じ関数で処理できる所が、纏められずに冗長なコードになっています。
これらのブラシュアップで、ブラウザへの負担を減らし、処理速度(見て判るほどではないにしろ)を改善できると思います。 また、「MutationObserver」の最適化の必要な部分があれば、再考したいと思っています。