2

我可以检测history.pushState来自 chrome 用户脚本(而不是扩展名)的 url 更改,而无需轮询(或打破脚本的孤立世界以拦截网页的 javascript 上下文中的调用)吗?

我的问题,更详细:

由于Github通过pjax [最小化示例]使用history.pushState[ WHAT-WG 规范]进行站点内页面到页面站点导航,而不是在您浏览站点时重新加载整个页面,用户脚本针对站点上的特定 url只有当您在该网址进入网站或手动强制重新加载页面时才会触发。

我正在为 DRY 编写一个on.js的小用户脚本库:整理用户脚本,通过拦截调用来为他们处理这类事情,pushState以查看我们何时在主机脚本想要运行的新页面上并仅调用它相关的。

如果在调用时触发事件,这将相对容易history.pushState,但据我所知,popstate除非第一页加载或用户触发历史记录的后退/前进移动,否则不会触发任何事件,这对我的目的来说毫无用处。

我也探索过将其限制为仅 pjax/github 的解决方案——当一个新页面加载时,github 会触发一个$(document).trigger('pageUpdate')事件,但是由于在jQuery内部而不是通过 DOM 事件实现这些,我听不到来自我的用户脚本运行的孤立世界的那些在,除非我在页面本身中注入一个小监听器,让它的jQuery实例向我报告这个事件,然后我还不如劫持页面history.pushState本身。

有没有人看到其他整洁的方法来实现这一目标?

4

2 回答 2

0

目前,我正在采用一种不太出色但有效的后一种方法,即注入一个脚本,该脚本拦截history.pushState调用并在调用完成后触发文档上的同名事件(并且,可选地,在 pjaxpageUpdate事件也触发之后) ):

function inject(fn, args) {
  var script = document.createElement('script')
    , parent = document.documentElement;
  args = JSON.stringify(args || []).slice(1, -1);
  script.textContent = '('+ fn +')('+ args +');';
  parent.appendChild(script);
  parent.removeChild(script);
}

inject(function(pjax_event) {
  function reportBack() {
    var e = document.createEvent('Events');
    e.initEvent('history.pushState', !'bubbles', !'cancelable');
    document.dispatchEvent(e);
  }
  var pushState = history.pushState;
  history.pushState = function on_pushState() {
    if (pjax_event && window.$ && $.pjax)
      $(document).one(pjax_event, reportBack);
    else
      setTimeout(reportBack, 0);
    return pushState.apply(this, arguments);
  };
}, ['pageUpdated']);

document.addEventListener('history.pushState', function() {
  console.log('history.pushState', location.pathname);
  // user script code here
}, false);
于 2012-11-07T08:30:56.847 回答
0

在 GreaseMonkey 更改它的沙盒策略之前(阅读:Firefox 做了,而 GM 没有使其向后兼容)可以unsafeWindow用来获取元素/窗口事件。现在已经改变了,unsafeWindow 在与GreaseMonkey API一起使用时不再起作用。

您提出的将脚本注入页面的答案在 Github 上不起作用,因为它非常严格的内容安全策略 (CSP) 不允许内联脚本;

内容安全策略:默认源代码 *;script-src assets-cdn.github.com 收集器-cdn.github.com;[...]

对于两种不同的情况,我的解决方案是双重的:一种使用@grant noneGreaseMonkey API ,另一种使用 GreaseMonkey API @grant

test-grant-none.user.js

// ==UserScript==
// @name      test @grant none
// @namespace https://github.com/jerone/UserScripts
// @include   https://github.com/*
// @version   1
// @grant     none
// ==/UserScript==

unsafeWindow.$(document).on("pjax:end", function() {
    console.log('test @grant none 1');
});
unsafeWindow.$(window).on('popstate', function() {
    console.log('test @grant none 2');
}); 

测试授权-GM.user.js

// ==UserScript==
// @name      test @grant GM_*
// @namespace https://github.com/jerone/UserScripts
// @include   https://github.com/*
// @version   1
// @grant     GM_setValue
// ==/UserScript==

unsafeWindow.$(document).on("pjax:end", exportFunction(function() {
    console.log('test @grant GM_* 1');
}, unsafeWindow));
unsafeWindow.$(window).on('popstate', exportFunction(function() {
    console.log('test @grant GM_* 2');
}, unsafeWindow)); 

你可以在这里找到上面的脚本:https ://gist.github.com/jerone/e38e8637887559870d84

你可以在这里阅读更多:https ://greasyfork.org/en/forum/discussion/3648/hook-into-jquery-existing-events

感谢 Athorcis 为我指明了正确的方向。

于 2015-04-01T11:34:49.223 回答