11

Google Chrome 拡張機能を作成していて、ページのタイトルがいつ変更されたかを検出する必要があります。ページのタイトルは Twitter のように変更されます: (num) Twitter(下のスクリーンショットを参照) - 新しいツイートが投稿されると、数字が増えます。例:

ここに画像の説明を入力

タブの 1 つに読み込まれた URL のタイトルの変更を検出し、違いがあるたびにビープ音を鳴らそうとしています。このチェックは繰り返し行われるべきであり、関数を使用して達成できると思いsetTimeOut()ます。

私はmanifest.json次のように作成しました:

{
  "manifest_version": 2,

  "name": "Detect Page Title Changes",
  "description": "Blah",
  "version": "1.0",

  "browser_action": {
    "default_icon": "icon.png",
    "default_popup": "background.html"
  },
  "permissions": [
    "tabs"
  ]
}

しかし、私は残りについて無知です。私はドキュメントを検索し、このような同様のスタック オーバーフロー スレッドで解決策を試しましたが、私の要件に合ったものは見つかりませんでした。1 2

何か提案はありますか?可能であれば、例を含めてください。

4

4 に答える 4

18

特定のアプローチの方が優れているとコメントで主張する代わりに、より建設的になり、私が共同で作成した特定の実装を示して回答を追加し、遭遇する可能性のある落とし穴を説明します。コード スニペットは Twitter とは異なるサービスを参照していますが、目的は同じです。実際、このコードの目的は未読メッセージの正確な数を報告することなので、あなたのコードはもっと単純かもしれません。

私のアプローチはSO に関する回答に基づいており、ポーリング駆動型(一定間隔で状態をチェックする)ではなく、イベント駆動型(状態の潜在的な変化が通知される) です。

利点には、変更の即時検出 (そうでなければ次のポーリングまで検出されない) と、条件が変更されていない間、ポーリングでリソースを浪費しないことが含まれます。確かに、2 番目の議論はここではほとんど当てはまりませんが、最初の議論は依然として有効です。


アーキテクチャの概要:

  1. 問題のページにコンテンツ スクリプトを挿入します。

  2. タイトルの初期状態を分析し、 を介してバックグラウンド ページにレポートしますsendMessage

  3. タイトル変更イベントのハンドラを登録します。

  4. イベントが発生してハンドラーが呼び出されるたびに、タイトルの新しい状態を分析し、 を介してバックグラウンド ページに報告しますsendMessage


すでにステップ 1 には落とし穴があります。通常のコンテンツ スクリプト インジェクション メカニズムは、コンテンツ スクリプトがマニフェストで定義されている場合、URL に一致するページへのナビゲーション時にページに挿入します。

"content_scripts": [
  {
    "matches": [
      "*://theoldreader.com/*"
    ],
    "js": ["observer.js"],
    "run_at": "document_idle"
  }
]

拡張機能がリロードされるまで、これはかなりうまく機能します。これは、行った変更を適用している開発中、または自動更新されているデプロイされたインスタンスで発生する可能性があります。その場合、コンテンツ スクリプトは既存の開いているページに再挿入されません(リロードなどのナビゲーションが発生するまで)。したがって、マニフェスト ベースのインジェクションに依存している場合は、拡張機能の初期化時に既に開いているタブへのプログラムによるインジェクションを含めることも検討する必要があります。

function startupInject() {
  chrome.tabs.query(
    {url: "*://theoldreader.com/*"},
    function (tabs) {
      for (var i in tabs) {
        chrome.tabs.executeScript(tabs[i].id, {file: "observer.js"});
      }
    }
  );
}

一方、拡張機能のリロード時にアクティブだったコンテンツ スクリプト インスタンスは終了せず、孤立します。すべてのsendMessageリクエストまたは同様のリクエストは失敗します。したがって、親拡張機能と通信しようとするときは常に例外をチェックし、失敗した場合は (ハンドラーを削除して) 自己終了することをお勧めします。

try {
  chrome.runtime.sendMessage({'count' : count});
} catch(e) { // Happens when parent extension is no longer available or was reloaded
  console.warn("Could not communicate with parent extension, deregistering observer");
  observer.disconnect();
}

ステップ 2 にも落とし穴がありますが、視聴しているサービスの詳細によって異なります。コンテンツ スクリプトの範囲内の一部のページでは、未読アイテムの数が表示されませんが、新しいメッセージがないという意味ではありません。

Web サービスがどのように機能するかを観察した結果、タイトルがナビゲーションのないものに変更された場合、正しい場合は新しい値を想定しても安全ですが、最初のタイトルの「新しいアイテムはありません」は信頼できないとして無視する必要があるという結論に達しました。

したがって、分析コードは、それが最初の読み取りであるか、更新の処理であるかを説明します。

function notify(title, changed) {
  // ...
  var match = /^\((\d+)\)/.exec(title);
  var match_zero = /^The Old Reader$/.exec(title);

  if (match && match[1]) {
    count = match[1];
  } else if (match_zero && changed) {
    count = 0;
  }
  // else, consider that we don't know the count
  //...
}

手順 2で、最初のタイトルとchanged=で呼び出されます。false


ステップ 3 と 4 は、「タイトルの変更を監視する方法」(イベント駆動型の方法) に対する主な答えです。

var target = document.querySelector('head > title');

var observer = new window.MutationObserver(
  function(mutations) {
    mutations.forEach(
      function(mutation){
        notify(mutation.target.textContent, true);
      }
    );
  }
);

observer.observe(target, { subtree: true, characterData: true, childList: true });

特定のオプションobserver.observeが設定されている理由の詳細については、元の回答を参照してください。

は=でnotify呼び出されるため、ナビゲーションなしで「(1) The Old Reader」から「The Old Reader」に移動すると、未読メッセージがゼロになる「真の」変更と見なされることに注意してください。changedtrue

于 2014-04-10T13:29:33.633 に答える
7

Chrome のタブ API を調査したところ、直接役立つものは何もないように見えます。ただし、関心のあるタブのタイトル ノードにイベント リスナーをアタッチできるはずです。 DOMSubtreeModified ミューテーション イベントは Chrome で機能し、通常の HTML ドキュメントでの簡単なテストが機能することが証明されています。拡張機能内と変わらないはずです。

var title = document.getElementsByTagName('title')[0];

if (title) {
    title.addEventListener('DOMSubtreeModified', function (e) {
        // title changed
    }, false);
}

于 2013-07-27T16:24:18.277 に答える