如何从扩展程序中检测Google Chrome中的页面标题更改?

Ama*_*ali 9 javascript google-chrome google-chrome-extension

我正在创建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"
  ]
}
Run Code Online (Sandbox Code Playgroud)

但是,我对其余的一无所知.我已经搜索了文档并尝试了类似Stack Overflow线程的解决方案,比如这个但我找不到任何符合我要求的东西.1 2

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

Xan*_*Xan 17

不要在评论中争论某种方法更好,让我更具建设性,并通过展示我自己共同编写的特定实现来添加答案,并解释一些可能遇到的问题.代码片段是指与Twitter不同的服务,但目标是相同的.实际上,此代码的目标是报告未读邮件的确切数量,因此您的邮件可能更简单.

我的方法基于SO 的答案,而不是轮询驱动(以固定间隔检查条件)是事件驱动的(被告知条件的潜在变化).

优点包括立即检测到变化(否则在下次轮询之前不会被检测到)并且在条件不变的情况下不会在民意调查上浪费资源.不可否认,第二个论点在这里几乎没有适用,但第一个论点仍然存在.


建筑一览:

  1. 将内容脚本注入相关页面.

  2. 分析标题的初始状态,通过后台报告sendMessage.

  3. 注册标题更改事件的处理程序.

  4. 每当事件触发并调用处理程序时,分析标题的新状态,通过后台报告到后台页面sendMessage.


第1步已经有了它的问题.正常内容脚本注入机制,当清单中定义内容脚本时,将在导航到与URL匹配的页面时将其注入页面.

"content_scripts": [
  {
    "matches": [
      "*://theoldreader.com/*"
    ],
    "js": ["observer.js"],
    "run_at": "document_idle"
  }
]
Run Code Online (Sandbox Code Playgroud)

这很有效,直到你的扩展重新加载.这可能在开发过程中发生,因为您正在应用您所做的更改,或者在已部署的实例中进行自动更新.然后会发生的是内容脚本不会在现有的打开页面中重新注入(直到导航发生,就像重新加载一样).因此,如果您依赖基于清单的注入,则还应考虑在扩展初始化时将编程注入包含在已打开的选项卡中:

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

另一方面,在扩展重新加载时处于活动状态的内容脚本实例不会终止,而是孤立的:任何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();
}
Run Code Online (Sandbox Code Playgroud)

第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
  //...
}
Run Code Online (Sandbox Code Playgroud)

在步骤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 });
Run Code Online (Sandbox Code Playgroud)

有关为何observer.observe设置某些选项的具体信息,请参阅原始答案.

请注意,notify使用changed= 调用true,因此从"(1)旧读者"到"没有导航的旧读者"被认为是对未读消息的"真实"更改.


lou*_*989 8

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

chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
   console.log(changeInfo);
});
Run Code Online (Sandbox Code Playgroud)

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

控制台中changeInfo对象的屏幕截图

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

  • 到目前为止,这是最好的答案.非常被低估了. (4认同)

Chr*_*and 7

  1. 创建一个活动页面.

  2. 创建一个内容脚本,在加载网页时将其注入网页.

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

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

  5. 在您的活动页面上,收听消息chrome.runtime.onMessage并播放声音.


小智 7

在研究了Chrome的标签API之后,看起来并没有什么能够直接帮助您.但是,您应该能够将事件监听器附加到您感兴趣的选项卡的标题节点.DOMSubtreeModified变异事件在Chrome中有效,并且在普通的html文档中进行快速测试证明对我有用 - 应该与扩展内容没有区别.

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

if (title) {
    title.addEventListener('DOMSubtreeModified', function (e) {
        // title changed
    }, false);
}
Run Code Online (Sandbox Code Playgroud)