前言
在我们在搜索时,可能会根据自己的需要,选择不同的搜索引擎。笔者这里就为之前开发的新标签页插件添加一个能够在google、baidu、bing等不同搜索引擎的搜索结果页自由切换的功能,帮助我们在多个搜索引擎之间快速切换,无需重复输入关键词。具体效果如下:
下面我介绍下这个功能的简单实现。
如何识别不同搜索引擎的关键词
观察不同的搜索引擎,我们能整理出下面这样的映射:
/**
* 搜索引擎映射
* @typedef {Object} SearchEngine
* @property {string} name - 搜索引擎的名称
* @property {string} url - 搜索引擎的查询 URL 模板
* @property {string} query - 查询参数的键名
* @property {string} icon - 搜索引擎的图标 URL
*/
/**
* @type {Object.<string, SearchEngine>}
*/
const SE = {
baidu: {
name: "百度",
url: "https://www.baidu.com/s?wd=",
query: "wd",
icon: "https://www.baidu.com/favicon.ico"
},
google: {
name: "谷歌",
url: "https://www.google.com.hk/search?q=",
query: "q",
icon: "https://www.google.com.hk/favicon.ico"
},
bing: {
name: "必应",
url: "https://cn.bing.com/search?q=",
query: "q",
icon: "https://cn.bing.com/sa/simg/bing_p_rr_teal_min.ico"
},
sougou: {
name: "搜狗",
url: "https://www.sogou.com/web?query=",
query: "query",
icon: "https://sogou.com/images/logo/new/favicon.ico?nv=1&v=3"
},
360: {
name: "360",
url: "https://www.so.com/s?q=",
query: "q",
icon: "https://www.so.com/favicon.ico"
}
}
其中url为不同搜索引擎的路由,query为不同搜索引擎获取关键词的query参数。得到这样一份映射以后,我们可以整理出下面这样的思路:
获取当前页面location信息,判断当前属于哪种搜索引擎,从而拿到对应的关键词,将关键词拼接到映射中对应的链接,即对应着关键词在某个搜索引擎下的链接。
如何实现UI界面让用户在不同搜索引擎结果页之间跳转
这里需要用到浏览器插件中的content_scripts功能,至于content_scripts功能的介绍,我在之前的文章写过(上文),这里不做赘述,这里直接上代码。
let curSE = getCurSE() // 存储当前搜索引擎
let keyword = "" // 存储当前搜索关键字
/**
* 搜索引擎映射
* @typedef {Object} SearchEngine
* @property {string} name - 搜索引擎的名称
* @property {string} url - 搜索引擎的查询 URL 模板
* @property {string} query - 查询参数的键名
* @property {string} icon - 搜索引擎的图标 URL
*/
/**
* @type {Object.<string, SearchEngine>}
*/
const SE = {
baidu: {
name: "百度",
url: "https://www.baidu.com/s?wd=",
query: "wd",
icon: "https://www.baidu.com/favicon.ico"
},
google: {
name: "谷歌",
url: "https://www.google.com.hk/search?q=",
query: "q",
icon: "https://www.google.com.hk/favicon.ico"
},
bing: {
name: "必应",
url: "https://cn.bing.com/search?q=",
query: "q",
icon: "https://cn.bing.com/sa/simg/bing_p_rr_teal_min.ico"
},
sougou: {
name: "搜狗",
url: "https://www.sogou.com/web?query=",
query: "query",
icon: "https://sogou.com/images/logo/new/favicon.ico?nv=1&v=3"
},
360: {
name: "360",
url: "https://www.so.com/s?q=",
query: "q",
icon: "https://www.so.com/favicon.ico"
}
}
// 获取当前搜索引擎
function getCurSE() {
let curSE = ""
const host = window.location.host
if (host === "www.baidu.com") {
curSE = "baidu"
} else if (host.includes("google")) {
curSE = "google"
} else if (host.includes("bing.com")) {
curSE = "bing"
} else if (host.includes"sogou") {
curSE = "sougou"
} else if (host.includes("www.so.com")) {
curSE = "360"
}
return curSE
}
// 获取搜索关键字
function getSearchKeyword(curSE) {
const search = window.location.search
const query = new URLSearchParams(search)
return query.get(SE[curSE].query)
}
// 生成搜索引擎a标签
function createSEItem(name, url, key) {
const item = document.createElement("div")
item.classList.add("ovo-search-engine-nav-item")
item.innerHTML = `<img src="${SE[key].icon}" alt="${name}" >`
// a标签
const a = document.createElement("a")
a.href = url
a.innerHTML = `${name}`
a.target = "_self"
a.setAttribute("data-key", key)
item.append(a)
return item
}
// 获取搜索引擎导航
function generateSearchEngineNav(curSE) {
const searchEngineNav = document.createElement("div")
searchEngineNav.classList.add("ovo-search-engine-nav")
keyword = getSearchKeyword(curSE)
if (!keyword) return
const navs = Object.keys(SE).map((key) => {
const se = SE[key]
return createSEItem(se.name, se.url + keyword, key)
})
searchEngineNav.append(...navs)
document.body.appendChild(searchEngineNav)
}
// 更新搜索引擎导航
function updateSearchEngineNav(curSE) {
// 找到原本的侧边栏
const searchEngineNav = document.querySelector(".ovo-search-engine-nav")
keyword = getSearchKeyword(curSE)
if (!keyword) return
if (searchEngineNav) { // 更新keyword
const navs = searchEngineNav.querySelectorAll(".ovo-search-engine-nav-item")
navs.forEach((nav) => {
const aDom = nav.querySelector("a")
const key = aDom.getAttribute("data-key")
const url = SE[key].url + keyword
aDom.href = url
})
} else { // 如果侧边栏丢失了 需重新生成
generateSearchEngineNav(curSE)
}
}
const debounceUpdateSearchEngineNav = debounce(updateSearchEngineNav, 1000)
// 监听 DOM 变化回调
function seMutationObserverCallback() {
requestAnimationFrame(() => {
// 获取当前的搜索引擎
curSE = getCurSE()
// 更新侧边栏
curSE && debounceUpdateSearchEngineNav(curSE)
})
}
// 监听 DOM 变化
function setupChangeSEMutationObserver() {
const observer = new MutationObserver(seMutationObserverCallback)
const config = { childList: true, subtree: true } // 监听子节点变化 和 所有后代节点变化
observer.observe(document.body, config)
}
// 防抖
function debounce(fn, delay) {
let timer = null
return function () {
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, arguments)
}, delay)
}
}
// 判断是否启用功能
window.chrome.storage.sync.get(["enable"], (result) => {
if (result.enable) {
// 生成切换搜索引擎的侧边栏
generateSearchEngineNav(curSE)
// 启动监听 保证在翻页、重新搜索过程中搜索词正确
setupChangeSEMutationObserver()
}
})
至此,大功告成,具体效果如下图所示:
结语
最后,本文主要为技术分享,仅供学习,欢迎大家体验笔者独立开发的新标签页插件~