【问题标题】:How to detect page title change in Google Chrome from an extension?如何通过扩展检测 Google Chrome 中的页面标题更改?
【发布时间】:2013-07-27 05:28:03
【问题描述】:

我正在创建一个 Google Chrome 扩展程序,我需要检测页面标题何时发生变化。该页面的标题更改为 Twitter 中的:(num) Twitter(请参见下面的屏幕截图) - 当发布新推文时,数字会增加。示例:

我正在尝试检测加载到我的一个标签中的 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 并在类似的 Stack Overflow 线程(例如 this one)上尝试了解决方案,但找不到任何适合我要求的东西。

你有什么建议吗?如果可能,请提供一个示例。

【问题讨论】:

  • 进行投票时是否需要事件?你不能只存储最后一个标签标题并将其与当前比较吗?
  • 我认为使用事件是个好主意(如果有的话)。但如果没有,也可以使用您的想法。
  • 我对反对票感到困惑;投票者最好解释一下为什么他/她认为这是一个无益和/或不正确的回应,这样我也许可以改进它:)
  • Detect change in document title via Javascript 的可能重复项,并且接受的答案更好。
  • @Xan:我不知道你从哪里得到的印象是重复的。也许从标题来看?它不是重复的。请再次阅读整个问题。

标签: javascript google-chrome google-chrome-extension


【解决方案1】:

与其在 cmets 中争论某种方法更好,不如让我更具建设性,并通过显示我自己合写的 particular implementation 添加答案,并解释一些你可能运行的陷阱进入。 代码 sn-ps 指的是与 Twitter 不同的服务,但目标是相同的。实际上,此代码的目标是报告未读消息的确切数量,因此您的可能更简单。 p>

我的方法基于 SO 上的an answer,而不是 轮询驱动(以固定间隔检查条件)是 事件驱动(通知条件的潜在变化)。

优点包括立即检测到更改(否则在下一次轮询之前不会检测到),并且在条件不变的情况下不会在轮询上浪费资源。诚然,第二个论点在这里几乎不适用,但第一个仍然成立。


架构一目了然:

  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 某些选项的具体原因,请参阅original answer

请注意,notify 是用 changed = true 调用的,因此从“(1) The Old Reader”到“The Old Reader”没有导航被认为是“真实”更改为零未读消息。

【讨论】:

  • @AmalMurali observer.js 几乎没有问题,只需修改 URL 和标题匹配模式以适应您的 Web 服务。暂时忘掉storage.js 和选项页面的工作原理吧,去掉它们,你就可以丢弃处理onMessage 以外的事情的大部分背景代码(background.jsfunctions.js)。其余的 - 您需要实现自己的逻辑来处理报告的计数,并适应 manifest.json。这是一个起点,祝你好运。
  • 不错的答案,一个建议:使用不带前缀的window.MutationObserver。如果您想支持 Chrome 26 或更早版本,您只需要添加 window.MutationObserver=window.MutationObserver||window.WebKitMutationObserver;,但鉴于 the Chrome Web Store now refuses to install extensions in Chrome 30 or earlier,您可能只使用不带前缀的 API 就可以了。
  • @RobW 不错。事实上,我注意到了,在代码库中修复了它,然后忘了在这里更新。
【解决方案2】:

chrome.tabs.onUpdated.addListener 放入您的后台脚本中:

chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
   console.log(changeInfo);
});

changeInfo 是一个包含标题更改的对象,例如这里:

然后可以过滤对象,以便仅当changeInfo 包含标题更改时才会发生操作。对于额外的操作,例如通过页面内容/操作响应页面标题更改,您可以在满足任何条件后从侦听器内部向内容脚本发送消息。

【讨论】:

  • 这是迄今为止最好的答案。被低估了。
【解决方案3】:
  1. 创建一个event page

  2. 创建一个content script,在网页加载时注入网页。

  3. 在内容脚本中,使用setInterval 轮询页面以查看window.document.title 是否更改。

  4. 如果标题已更改,请使用chrome.runtime.sendMessage 向您的活动页面发送消息。

  5. 在您的活动页面上,收听带有chrome.runtime.onMessageplay a sound 的消息。

【讨论】:

    【解决方案4】:

    在研究了 Chrome 的选项卡 API 之后,似乎没有什么可以直接帮助您。但是,您应该能够将事件侦听器附加到您感兴趣的选项卡的标题节点。 DOMSubtreeModified 突变事件在 Chrome 中有效,并且在普通 html 文档中进行快速测试证明对我有用 -应该与扩展内没有什么不同。

    var title = document.getElementsByTagName('title')[0];
    
    if (title) {
        title.addEventListener('DOMSubtreeModified', function (e) {
            // title changed
        }, false);
    }
    

    【讨论】:

    • 这是一个旧答案;从那时起,DOMSubtreeModified 事件被视为已弃用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-10
    • 1970-01-01
    • 1970-01-01
    • 2012-09-05
    • 2011-12-01
    相关资源
    最近更新 更多