周末开发了一个在 GitHub 中给 repo 增加自定义备注的 chrome 扩展。

开发这个扩展的原因是我在 GitHub 中所 star 的项目实在太多了(截止目前 671 个),有的项目过个几天回来看就忘了为什么 star 了,有的轮子想找的时候发现忘记叫什么了,这么多一个个找实在浪费时间。于是我一直在想有这么个工具,可以自定义对 GitHub 中的项目进行备注,然后可以根据备注进行搜索,于是便有了这个扩展。

如需安装体验请点击 GitHub Remarks 进行安装,源码移步 github-remarks(没啥参考价值)。

当然本文并不是讲这个扩展的制作过程,而是在这过程中碰到的一个问题。

以我的 GitHub 账户举例,在 https://github.com/hanzichi?tab=stars 页面,我需要插入一些 dom,使得页面如下:

乍一想,似乎很简单,在这个页面插入 content.js 就行,在 manifest.json 中增加配置:

{
  "matches": ["*://github.com/.*tabs=stars"],
  "js": ["content-scripts/repoDetail.js"],
  "css": [],
  "run_at": "document_end"
 }

但是问题来了,直接打开页面 https://github.com/hanzichi?tab=stars 并没有问题,但是如果从 https://github.com/hanzichi 点击跳转到 https://github.com/hanzichi?tab=stars,因为 GitHub 用了 pjax 技术,content.js 其实应该加载在页面 https://github.com/hanzichi 的,但是那个页面并没有参照的 dom 结构(好把我们需要的 dom 插入)。所以问题似乎就转为了,在 pjax 页面,如何能够监听到页面 url 的变化

首先一个很简单的方案是,弄个定时器循环监听,但是太耗费性能了,pass

因为页面是 pjax 跳转,所以 hashchange 这样的事件自然也用不上;而 pushstate 事件是监听浏览器前进后退的,所以也并不符合场景

有个想法很好,重写 pushState 事件(代码来自 How to detect when HTML5's history.pushState() is called?):

(function(history){
    var pushState = history.pushState;
    history.pushState = function(state) {
        if (typeof history.onpushstate == "function") {
            history.onpushstate({state: state});
        }
        // ... whatever else you want to do
        // maybe call onhashchange e.handler
        return pushState.apply(history, arguments);
    }
})(window.history);

但是因为 chrome 扩展和源代码之间并不共享作用域,所以重写的 history.pushState 方法在原来页面其实并没有改变,所以也没什么卵用

然后从 chrome API 入手,发现有个 chrome API 可以检测当前 url 改变的 tab(参考 Chrome extension WebNavigation listener for hash change):

chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
  if (changeInfo.url) {
      console.log('Tab %d got new URL: %s', tabId, changeInfo.url)
  }
})

这段代码是在 background.js 中,然后一旦侦测到 URL 变化,background.js 和 content.js 通信告知即可。

但是,但是,也有问题,url 一改变确实能监听到,但是 chrome 扩展需要在相应 dom ready 后才能作用(才能在之前 dom 的基础上插入新的 dom),你无法检测到什么时候 dom 已经准备就绪了,所以在这里加个定时器延迟作用是可行的,时间需要自己估算下

这个方法不是很优雅,最后想到,pjax 的过程中,dom 肯定有变化,所以监听 dom 变化是否可行?答案是可以的:

MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

var observer = new MutationObserver(function(mutations, observer) {
  let url = location.href
  let p = /.*\/\/github.com\/.*\?tab=stars.*/

  if (p.test(url)) {
    initStarsPage()
  }
})

observer.observe(document.getElementById('js-pjax-container'), {
  childList: true
})

$(document).ready(() => {
  let url = location.href
  let p = /.*\/\/github.com\/.*\?tab=stars.*/

  if (p.test(url)) {
    initStarsPage()
  }
})

写到最后,其实并不是监听了 URL,而是监听了 dom 的变化。

最后的最后,意外在 so 上找到 这个答案 似乎就是对应我的问题,大概看了下,除了我举例的几个方案外,其实我遗漏了最简单的一个方案,既然页面使用了 pjax,那么直接监听 pjax:complete 事件即可:

$(document).on('pjax:complete', function() {
  let url = location.href
  let p = /.*\/\/github.com\/.*\?tab=stars.*/

  if (p.test(url)) {
    initStarsPage()
  }
})

突然让人感觉,有时候 "真相" 往往总是那么简单。

对于单页应用中如何监听 URL 变化的思考的更多相关文章

  1. 009——VUE中watch监听属性变化实现类百度搜索栏功能

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  2. Angular.js中使用$watch监听模型变化

    $watch简单使用 $watch是一个scope函数,用于监听模型变化,当你的模型部分发生变化时它会通知你. $watch(watchExpression, listener, objectEqua ...

  3. 新建Oracle数据库时,提示使用database control配置数据库时,要求在当前oracle主目录中配置监听程序

    新建一个oracle数据库时,当提示使用database control配置数据库时,要求在当前oracle主目录中配置监听程序等字样的时候,问题是那个监听的服务没有启动,解决方法如下: 打开cmd命 ...

  4. Android中如何监听GPS开启和关闭

    转自 chenming 原文 Android中如何监听GPS开启和关闭   摘要: 本文简单总结了如何监听GPS开关的小技巧 有时需要监听GPS的开关(这种需求并不多见).实现的思路是监听代表 GPS ...

  5. Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差

    Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差别   Nginx连接fastcgi的方式有2种:unix domain socket和TCP,Uni ...

  6. oracle 11g在安装过程中出现监听程序未启动或数据库服务未注册到该监听程序

    15511477451 原文 oracle 11g在安装过程中出现监听程序未启动或数据库服务未注册到该监听程序? 环境:win7 64位系统.oracle11g数据库 问题描述:在win7 64位系统 ...

  7. Android 关于ListView中按钮监听的优化问题(方法二)

    关于ListView中按钮监听的优化问题(方法一)地址: http://www.cnblogs.com/steffen/p/3951901.html 之前的方法一,虽然能够解决position的传递, ...

  8. Android 关于ListView中按钮监听的优化问题(方法一)

    在Android应用开发过程中经常会用到ListView,并且每次在item中都要对点击事件进行监听.在给按钮添加OnClickListener时,一般会下意识的在getView()中找到每一个But ...

  9. 如何在vue单页应用中使用百度地图

    作为一名开发人员,每次接到开发任务,我们首先应该先分析需求,然后再思考技术方案和解决方案.三思而后行,这是一个好的习惯. 需求:本项目是采用vue组件化开发的单页应用项目,现需要在项目中引入百度的地图 ...

随机推荐

  1. [C#]使用 C# 编写自己的区块链挖矿算法

    [C#] 使用 C# 编写自己区块链的挖矿算法 文章原文来自:Code your own blockchain mining algorithm in Go! ,原始文章通过 Go 语言来实现的,这里 ...

  2. 完整的WebRTC调用序列图

    说在前面的话:此图出自Rea-Time Communication with WebRTC: https://book.douban.com/subject/25849712/ 的第五章.

  3. python3.6 安装win32api时候找不到regitry的问题

    首先下载 https://sourceforge.net/projects/pywin32/files/pywin32/ 找到对应的即可 我需要的是这个 打开之后会提示3.6未注册 在任意位置新建一个 ...

  4. jquery touch 移动端上下滑动加载

    var touchStart, touchEnd, touchDiff = 80; $(window).on({ 'touchstart': function (e) { touchStart = e ...

  5. API文档模板

    接口说明: 登录接口 修订历史: 版本号 制定人 修订日期 0.0.2 zenghui 2017-09-27 0.0.1 zanshan 2017-02-20 URL: http://xxx.xx.c ...

  6. zfs文件系统简单使用

    关于ubuntu下zfs的使用参考:https://github.com/zfsonlinux/zfs/wiki/Ubuntu%2016.04%20Root%20on%20ZFS 安装zfs: 启动z ...

  7. 读《图解HTTP》有感-(确保WEB安全的HTTPS)

    写在前面 该章节分析当前使用的HTTP协议中存在的安全性问题,以及采用HTTPS协议来规避这些可能存在的缺陷 正文 1.HTTP的缺点 1.1.由于HTTP不具备加密功能,所以在通信链路上,报文是以明 ...

  8. git进阶

    一.刚提交的代码,发现需要微调一下 刚刚最新提交了一段代码,然后跟前端说,接口好了.过了2分钟,前端跟你说,哎,兄弟,那个金额能不能返回整数,不要小数点. 这个时候一般我们通常会修改一下之后,再提交一 ...

  9. Java 8 文件操作(转)

    我们知道在JDK6甚至之前的时候,我们想要读取一个文本文件也是非常麻烦的一件事,而现在他们都变得简单了, 这要归功于NIO2,我们先看看之前的做法: 读取一个文本文件 BufferedReader b ...

  10. Tiny4412之重力感应器驱动

    一:Tiny4412 裸板重力感应驱动编写 整个编写过程大概分为如下几步: (1)在底板上找到重力感应器的所在位置,找到芯片型号(我用的板子上重力感应器芯片型号为:MMA7660FC) (2)通过型号 ...