「MutationObserver」とは 

ページの閲覧者側で動作するJavaScript(ユーザースクリプト)の実用的プログラムでは、多くの場合に「MutationObserver」が必要になります。

 

この機能は、ページ上の特定要素の状態を監視して、その変化を感知したら指定した関数を実行させる事に使います。 現代のウェブページは、表示の一部が変化する構成のページが多く、その度にページ上に新しい要素が出現します。 この新しい要素に対して JavaScriptは機能できず、いわゆる「発火しない」状態になります。 クリックすれば動作するはずのコードが、そこで終わってしまうのです。

 

この様な場合、新しい要素を取得しなおす事が必要です。 そのためには「新要素の出現」を監視して、出現をきっかけに動作する機能が必要です。 そういった事を可能にするのが「MutationObserver」で、この機能には「変異を監視する」といった名前が付けられています。

 

詳しくは以下のページが良く纏まって判り易いです。

 

 

 

 

過剰反応による自家中毒 

上記ページの最後に、この機能の前身で廃止になった「MutationEvent」の事が書かれていますが、「MutationObserver」を使う上でも同様の注意が要ります。

 

例えば「body」の子要素までを含めて監視対象要素にして、変化があればプログラム全体を再起動させる設定をした時、プログラムの中に表示要素を生成させる下りがあると、その生成に「MutationObserver」が反応し、もう一度処理を再起動し… と繰り返す事になります。 これは無限に続いてページはフリーズしてしまいます。

 

この様な状態に陥らなくても、想定した以上に「MutationObserver」が反応して、動作はするが妙に遅い、ぎこちない、といった状態になる事があります。

 

「MutationObserver」が不可欠の場合は多いのですが、同時にそのコントロールが重要になる事が多いのは事実です。

 

 

 

必要に駆られて 

現在、前ページの「Bad Iine Mute」の動作を検証中ですが、このツールでは7個の「MutationObserver」が機能しています。 特に複雑なのが、「管理トップ」「いいね!履歴」の実質3画面での動作の切換えで、「MutationObserver」の設定が悪いとツールが起動しなかったり、反対にページ読込みを遅延させたりと、落ち着きどころを探すのに苦労しています。

 

この「MutationObserver」の設定を最適化するため、「どのくらい機能しているか」を調べる事にしました。 目的とした動作をしているのか、過剰な動作をしていないかを、目視で判断しようという試みです。

 

先ず簡単に、ページの「body」の色を一瞬だけ「ブルー」に変化させる関数をコード上に作ってみました。 下がその例です。

 

function call(){
    document.body.style.background='#2196f3';
    setTimeout(()=>{
        document.body.style.background='inherit';
    }, 10); }

 

設定では、1/100 secだけの表示ですが、充分の様です。 この関数を、調査する「MutationObserver」が起動させる関数の最初に仕込みます。

 

下は実際の周辺コードです。

 

let target0=document.head; // 監視 target
let monitor0=new MutationObserver(mode_select);
monitor0.observe(target0, {childList: true}); // 監視開始

function mode_select(){
call();
    if(document.querySelector('#iineEntry')){ // 管理トップ
        ~~~~以下略

 

最初の3行が「MutationObserver」の設定で、ここでは「mode_select」という関数を「head」(ページのヘッダー)の変化に反応して再実行する設定にしています。

 

「mode_select」の再実行は、普通は画面上で判りません。 しかし「call()」をその先頭で実行する様にしたので、再実行ごとにページの背景色が青色になり、目視でどの程度「MutationObserver」が動作したが判ります。

 

下は、「管理トップ」のリロード後に、「Bad Iine Mute」の「mode_select()」がこの「MutationObserver」で再起動される様子です。

 

 

❶「いいね!された記事」のタイトル部が表示される。

タイトル部が表示された時には、「Bad Iine Mute」のアイコンがインポーズされているので、ここの青背景は「mode_select()」が最初に起動された事が判る。 しかし、青背景の時間が長めで、複数回起動されている可能性がある。

 

❷ 実際の「いいね」データのローディングマークが僅かの時間表示される。

これは、「いいね」のデータをシステムから受け取っている状態。

 

❸「いいね」の記事リストとアイコン群が表示され、この瞬間に一瞬青背景になる。

これは、アイコン等の表示に「MutationObserver」が反応して「mode_select()」が再起動されたと考えられる。

 

 

 

動作チェックコードを改善 

上のテストで、❶の時間が長いので、もしかしたらこの段階で複数回の実行があるのかと気になり、カウントコードを作りました。

 

let call_count=0;

function call(){
    call_count+=1;
    let call=
        '<div id="call_message">'+ call_count +'</div>'+
        '<style>'+
        '#call_message { position: fixed; top: 20px; left: 15px; z-index: 100; '+
        'font: bold 16px Meiryo; padding: 3px 15px 0; color: #000; background: #fff; }'+
        '</style>';

    let call_message=document.querySelector('#call_message');
    if(call_message){
        call_message.remove(); }
    document.body.insertAdjacentHTML('beforeend', call);

    document.body.style.background='#2196f3';
    setTimeout(()=>{
        document.body.style.background='none';
    }, 20); }

 

ページを開くごとにカウントはリセットされますが、「call()」を忍ばせた関数が、再起動されられる回数を画面の左上隅に累積して表示します。

 

下は、「管理トップ」ページのリロードの数秒後(上の動画と同じ)の結果で、再起動の回数は「2」回だと判りました。

 

 

ここで、「青背景」のリセットコードを「background='inherit'」から「'none'」に変更すると、❶ の青背景の時間の遅延がなくなりました。 JavaScriptからの指定は、ページの本来の指定より強力で、「inherit」はリセットにタイムラグが生じる場合がある様です。

 

 

 

ここからが本当の作業 

このページのテストコードを使い、「Bad Iine Mute」をもう少し煮詰め様と考えています。「Bad Iine Mute」は、合計 6個の画面で、「いいねブロック」のとても似たコードを動作させています。 しかし、それぞれの画面ごとに「いいね」リストのHtml構成が微妙に異なり、本来なら同じ関数で処理できる所が、纏められずに冗長なコードになっています。

 

これらのブラシュアップで、ブラウザへの負担を減らし、処理速度(見て判るほどではないにしろ)を改善できると思います。 また、「MutationObserver」の最適化の必要な部分があれば、再考したいと思っています。