|
// ==UserScript== |
|
// @name 知乎直答链接还原 |
|
// @namespace https://gist.github.com/ricky9w/46f5d65844401f93bd1398aa3122eb49 |
|
// @version 0.1.1 |
|
// @description 识别知乎回答/文章中被自动转换的「知乎直答」(zhida.zhihu.com) 链接,将其还原为普通文字,去除链接与四角星图标干扰。 |
|
// @author ricky9w |
|
// @updateURL https://gist.githubusercontent.com/ricky9w/46f5d65844401f93bd1398aa3122eb49/raw/zhihu-zhida-remover.user.js |
|
// @downloadURL https://gist.githubusercontent.com/ricky9w/46f5d65844401f93bd1398aa3122eb49/raw/zhihu-zhida-remover.user.js |
|
// @match https://www.zhihu.com/* |
|
// @match https://zhuanlan.zhihu.com/* |
|
// @run-at document-start |
|
// @inject-into content |
|
// @noframes |
|
// @grant none |
|
// ==/UserScript== |
|
|
|
(function () { |
|
'use strict'; |
|
|
|
// 「知乎直答」链接均指向 zhida.zhihu.com,以此作为最可靠的判别依据。 |
|
const SELECTOR = 'a[href*="zhida.zhihu.com"]'; |
|
|
|
/** |
|
* 提取链接的可见文字,排除尾部的四角星 <svg> 图标。 |
|
* 若文字为空(异常情况),回退到 URL 的 q 参数。 |
|
*/ |
|
function getLabel(a) { |
|
let text = ''; |
|
for (const node of a.childNodes) { |
|
if (node.nodeType === Node.TEXT_NODE) { |
|
text += node.nodeValue; |
|
} else if (node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() !== 'svg') { |
|
text += node.textContent; |
|
} |
|
} |
|
text = text.trim(); |
|
|
|
if (!text) { |
|
try { |
|
const q = new URL(a.href).searchParams.get('q'); |
|
if (q) text = q; |
|
} catch (_) { /* href 非法时忽略 */ } |
|
} |
|
return text; |
|
} |
|
|
|
/** 将单个直答链接替换为纯文字节点。 */ |
|
function restore(a) { |
|
const textNode = document.createTextNode(getLabel(a)); |
|
|
|
// 链接通常被一个无 class 的 <span> 单独包裹,连同一并移除更干净; |
|
// 但若处于 <b> 等语义标签内,则只替换 <a> 本身以保留加粗等样式。 |
|
let target = a; |
|
const parent = a.parentElement; |
|
if (parent && parent.tagName === 'SPAN' && !parent.className && parent.childNodes.length === 1) { |
|
target = parent; |
|
} |
|
target.replaceWith(textNode); |
|
} |
|
|
|
/** 扫描指定根节点下的所有直答链接并还原。 */ |
|
function scan(root) { |
|
if (root.nodeType !== Node.ELEMENT_NODE && root.nodeType !== Node.DOCUMENT_NODE) return; |
|
if (root.matches && root.matches(SELECTOR)) { |
|
restore(root); |
|
return; |
|
} |
|
root.querySelectorAll(SELECTOR).forEach(restore); |
|
} |
|
|
|
// 处理首屏(服务端渲染)已存在的内容。 |
|
scan(document); |
|
|
|
// 知乎为单页应用,回答/文章随滚动与路由动态加载,需持续监听新节点。 |
|
const observer = new MutationObserver((mutations) => { |
|
for (const m of mutations) { |
|
for (const node of m.addedNodes) { |
|
scan(node); |
|
} |
|
} |
|
}); |
|
|
|
observer.observe(document.documentElement, { childList: true, subtree: true }); |
|
})(); |