先看下插件效果
背景
最近被广进了,可以说是相当焦虑,疯狂刷各种招聘软件找面试机会,在刷 boss 直聘的时候发现有些岗位已经存在很久了,4年前我刚毕业时看到的岗位居然还在,属实有点离谱,想着有没有什么方式把岗位的时间给显示出来。
怎么获取时间
想要展示时间,那么第一步我得知道时间在哪,所以先去 web 端 boss 直聘去找接口,发现有个 joblist 接口返回列表数据,并且里面有个 lastModifyTime 的值,看名字应该是职位的最后修改时间,但不能确定这个时间就是岗位的修改时间。我这主要是对比了下 app 端有 new 标签的职位和 web 端这里的时间,看时间是否相近。对比下来感觉这个时间应该靠谱所以就使用了这个字段。(如果不对那就当熟悉下 chrome 插件的开发了😂
怎么展示时间
chrome 插件的基础开发就不介绍了,大家可以自己去找下相应的教程
1. 重写 XMLHttpRequest
既然需要数据那么就需要监听接口请求,这里是通过重写 XMLHttpRequest 来实现的,由于 content-script 脚本没办法直接修改原页面的 js,所以通过 script 标签插入自己的脚本(proxyAjax.js),在这个脚本里去重写 XMLHttpRequest。注意:proxyAjax 脚本必须插入在 head 标签第一个节点中,boss 直聘的页面里有也有重写 XMLHttpRequest 的操作,所以要提前去执行
let oldXHR = window.XMLHttpRequest;
function newXHR() {
let realXHR = new oldXHR();
realXHR.addEventListener('readystatechange', function(e) {
// 自定义的事件,抛出去
ajaxEventTrigger.call(this, 'ajaxReadyStateChange');
}, false);
}
2. 获取职位的 DOM
拿到数据后开始分析数据和对应的 DOM 有什么关联?假如是我们自己开发这个业务,很显然会去用数组去循环生成视图,那么一般来说会用一个 id 来作为 key 值,所以找到这个关联的 id。
分析下图的数据,可以比较明显看出来 li 标签的 ka 属性用的是 itemId(截图里还不是很明显,往后翻一页就会发现这两个能对上), search_list_${itemId}
,所以就可以直接用选择器去获取对应的dom
node.querySelector(`[ka="search_list_${itemId}"]`);
3. 插入时间标签
在上一步我们分析了怎么去获取对应的 DOM,获取到了就可以给 DOM 插入标签了,对整个数据进行循环,对每个元素获取一次 DOM,然后创建 div 插入到 DOM 当中
// 获取职位列表节点
function getListByNode(node) {
return function getListItem(itemId) {
return node.querySelector(`[ka="search_list_${itemId}"]`);
}
}
// 向 DOM 插入标签
function parseBossData(list, getListItem) {
list.forEach(item => {
const {
itemId, lastModifyTime,
} = item;
const time = dayjs(lastModifyTime).format('YYYY-MM-DD HH:mm:ss');
// 获取 DOM
const dom = getListItem(itemId);
// 生成标签
let tag = createDOM(time);
// 插入 DOM
dom.appendChild(tag);
});
}
// 生成标签
function createDOM(time) {
const div = document.createElement('div');
div.classList.add('__boss_time_tag');
div.innerText = time;
return div;
}
4. 结束了吗?
看上去好像完美了,但实际上发现没有生效,并且报错 node.querySelector is not a function。为什么会这样?如果看到这可以考虑下。
其实原因很简单,获取数据后到我们代码执行其实是同步的,此时视图对应的节点还没有创建出来,所以获取的 node 为 null。那么怎么保证 node 节点能拿到呢?js 提供了一个 MutationObserver api,通过这个来监听节点的变化,是否新增了节点,在确保新增之后再去运行我们之前的代码这样就保证了能拿到 node 节点。
// 监听 search-job-result 节点,判断职位列表是否被挂载
function mutationContainer () {
return new Promise((resolve, reject) => {
// 获取列表的父节点容器,监听该节点
const dom = document.querySelector('.search-job-result');
// 创建 MutationObserver 实例
const observer = new MutationObserver(function(childList, obs) {
(childList || []).forEach(item => {
const {
addedNodes
} = item;
if(addedNodes && addedNodes.length > 0) {
addedNodes.forEach(node => {
const {
className,
} = node;
if(className === 'job-list-box') {
observer.disconnect();
resolve(node);
}
})
}
});
return reject('未找到职位列表');
})
observer.observe(dom, {
childList: true,
subtree: false
})
})
}
// 回调的时候去执行插入标签操作
mutationContainer().then((node) => {
parseBossData(data?.zpData?.jobList || [], getListByNode(node));
})
结尾:
代码我放在 github 仓库了,有需要的可以去看看,如果觉得还可以顺便点个 star 吧,求求了
切换分支 gh-page 有已经编译好的插件,可以直接下载使用
github仓库 github.com/tangzhiyao/…