上一篇文章中,我在LeetCode抓取题解的油猴脚本中新增了一个功能,在题解页面,单独显示一个按钮,点击按钮之后可以抓取当前页面的题解内容。
到这里就产生了一个问题,如何判断当前页面是一个题解的页面呢,举一个简单的题解页面的URL的例子
https://leetcode.cn/problems/find-duplicate-subtrees/solution/by-cheungq-6-fkom/
我们可以看到,除去前面的“https://
”协议、“leetcode.cn
”域名,后面的“/problems/find-duplicate-subtrees/solution/by-cheungq-6-fkom/”部分就是我们需要解析的部分,这部分内容存在于“window.location.pathname”之中,我们可以随时很方便的获取到,只需判断下是不是
/problems/{Question_Slug}/solution/{Solution_Slug}/
这样的格式即可,当然如果你非常熟悉正则的方式,也可以用正则匹配判断。这样我们就可以知道这个页面是不是一个题解的页面了,如果当前页面是一个题解页面,则我们进行控制显示一个抓取当前题解的按钮
在解决了题解页面判断之后,重新回到LeetCode网站看下,这时会发现我们遇到了新的问题。LeetCode的题目和题解页面使用的是无刷新的方法来渲染页面的,点击题目或者题解内容,页面进行渲染,同时Url进行变更,页面不进行刷新操作。这样的方式大大提升了用户的浏览体验。而很明显我们可以看到使用的不是hash模式的URL,而是history模式的实现。
hash模式的URL是在URL结尾拼接上“#”符及相关参数来实现的,变更URL中“#”后面的内容不会引发页面刷新操作,“#”原本是作为页面锚点的功能存在的
熟悉前端的小伙伴应该对hash模式和history模式这两个概念非常了解了,基本属于面试必问基础题系列,如果不知道的可以再自行百度一下
那么在有了以上的基础认知之后,我们就开始着手对history模式URL变更进行监听操作
不同于hash模式的URL,当hash模式的URL发生变更的时候,会触发window.onhashchange
事件(参见Window:hashchange event),这样的话我们只要监听window.onhashchange
事件就可以了知道URL变更了。而history模式下,是通过history对象来操作实现的,根据MDN文档上的说明信息,在history操作的时候会触发popstate
事件,不过下面有一条额外的备注
调用 history.pushState()
或者 history.replaceState()
不会触发 popstate
事件
在力扣的网页上试了下window.addEventListener('popstate',function)
相关的操作,确实在点击了之后没有反应。
那么到这里我们又有了另一个思路,参照之前的【油猴脚本】关于用油猴脚本爬取考试题库这件事文章中使用的手法,我们自己建个replaceState和pushState方法替换掉原来history中的replaceState和pushState方法,在我们自己建的方法中调用原来浏览器的对应方法,并向外抛出一个相应的事件,于是就有了如下代码
let _historyWrap = function(type) {
const orig = history[type];
return function() {
const rv = orig.apply(this, arguments);
publishCustomEvent(type,arguments)
return rv;
};
};
history.pushState = _historyWrap('pushState');
history.replaceState = _historyWrap('replaceState');
其中的publishCustomEvent
方法是调用的之前的window.CustomEvent浏览器自定义事件文章中写的自定义浏览器事件相关的代码。
这样,我们只要再监听下这里抛出的pushState和replaceState事件就可以了,代码如下
window.addEventListener('pushState',(e)=>{
//do something
//判断当前url是否是题解页面
},true)