您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
使用 斜杠 ('/') 聚焦输入框,仅针对部分网页定制。
// ==UserScript== // @name Slash To Search // @name:zh-CN 斜杠搜索 - Slash To Search // @namespace http://zhangmaimai.com/ // @version 0.2 // @description Type Slash ('/') to focus on search input, customized for some websites only. // @description:zh-CN 使用 斜杠 ('/') 聚焦输入框,仅针对部分网页定制。 // @author Max // @license MIT // @match https://www.v2ex.com/* // @match https://stackoverflow.com/* // @match https://*.bilibili.com/* // @exclude https://message.bilibili.com/pages/nav/header_sync // @match https://www.douban.com/* // @match https://book.douban.com/* // @match https://*.wikipedia.org/* // @match https://gf.qytechs.cn/* // @exclude https://gf.qytechs.cn/*/* // @icon https://www.google.com/s2/favicons?sz=64&domain=gf.qytechs.cn // @grant none // @run-at document-idle // ==/UserScript== // @ts-check const { hostname } = window.location const secondLevel = hostname.split('.').slice(1).join('.') /** @typedef {Record<string, string>} StringMap */ /** * Map series of URLs with same selector * @param {string[]} urls * @param {string} selector */ const urlToSameSelector = (urls, selector) => Object.fromEntries(urls.map(url => [url, selector])) /** * Second level domain to a function to get selector by hostname * @type {Record<string, ()=>StringMap>} * */ const getSelectorMap = { "bilibili.com": () => Object.assign({ "manga.bilibili.com": ".search-input" }, urlToSameSelector([ 'bilibili.com', 'www.bilibili.com', 't.bilibili.com', 'space.bilibili.com', ], '.nav-search-input') ), "v2ex.com": () => ({ 'www.v2ex.com': '#search', }), "stackoverflow.com": () => ({ 'stackoverflow.com': '.s-input' }), "douban.com": () => ({ 'book.douban.com': '#inp-query', 'www.douban.com': '.inp input' }), "wikipedia.org": () => (urlToSameSelector([ 'zh.wikipedia.org', 'en.wikipedia.org' ], '.cdx-text-input__input', )), "gf.qytechs.cn": () => ({ "gf.qytechs.cn": ".home-search input" }) } /** * @param {string} selector * @param {HTMLElement | Document | Element} root * @param {number} timeout * @returns {Promise<Element>} */ const isElementLoaded = async (selector, root = document, timeout = 1e4) => { const start = Date.now() while (root.querySelector(selector) === null) { if (Date.now() - start > timeout) throw new Error(`Timeout: ${timeout}ms exceeded`) await new Promise(resolve => requestAnimationFrame(resolve)) } return /** @type {HTMLElement} */(root.querySelector(selector)) } /** @param {HTMLInputElement} search */ const addSlashEvent = (search) => { const exceptActiveElement = ['INPUT', 'TEXTAREA'] /** @type {(event: KeyboardEvent) => void } */ const listener = (e) => { if (e.key !== '/' || exceptActiveElement.includes(document?.activeElement?.tagName || "")) return e.preventDefault() search.focus() } document.addEventListener('keydown', listener) } const main = async () => { const getSelector = () => { if (hostname in getSelectorMap) return getSelectorMap[hostname]()[hostname] if (!(secondLevel in getSelectorMap)) return const selectorMap = getSelectorMap[secondLevel]() return hostname in selectorMap ? selectorMap[hostname] : selectorMap['*'] } const selector = getSelector() if (!selector) console.error(`No selector was found for url origin, downgrading to match <input> element with class contains "search"`) const searchElement = /** @type {HTMLDivElement?} */ (selector ? await isElementLoaded(selector) : await isElementLoaded('input[class*="search"]') ) if (!searchElement || !(searchElement instanceof HTMLInputElement)) { throw Error(`Cannot detect search input element with selector ${selector}`) } addSlashEvent(searchElement) } main()
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址