すべてのリンクをクリックできるリンクにかえる。
当前为
// ==UserScript==
// @name Convert Any Links to Clickable Links
// @namespace kdroidwin.hatenablog.com
// @version 2.8
// @description すべてのリンクをクリックできるリンクにかえる。
// @author Kdroidwin
// @match *://*/*
// @exclude *://github.com/*
// @exclude *://chat.openai.com/*
// @exclude *://blog.hatena.ne.jp/*
// @exclude *://w.atwiki.jp/*
// @grant none
// @license GPL-3.0
// ==/UserScript==
(function() {
'use strict';
const urlPattern = /\b(?:h?ttps?:\/\/[^\s<>"]+|(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(?:\/[^\s<>"]*)?)\b/g;
function convertTextToLinks(node) {
if (node.nodeType !== 3 || !urlPattern.test(node.nodeValue)) return;
const parent = node.parentNode;
if (parent.tagName === 'A' || parent.matches('input, textarea, [contenteditable]')) return;
const frag = document.createDocumentFragment();
let lastIndex = 0;
node.nodeValue.replace(urlPattern, (match, offset) => {
frag.appendChild(document.createTextNode(node.nodeValue.slice(lastIndex, offset)));
const a = document.createElement('a');
a.href = match.startsWith('ttp') ? 'h' + match : match.includes('://') ? match : 'https://' + match;
a.textContent = match;
a.target = '_blank';
frag.appendChild(a);
lastIndex = offset + match.length;
});
frag.appendChild(document.createTextNode(node.nodeValue.slice(lastIndex)));
parent.replaceChild(frag, node);
}
function debounce(func, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => func(...args), delay);
};
}
const observer = new MutationObserver(debounce(mutations => {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
for (const node of mutation.addedNodes) {
if (node.nodeType === 1) {
for (const textNode of node.childNodes) {
convertTextToLinks(textNode);
}
}
}
} else if (mutation.type === 'characterData') {
convertTextToLinks(mutation.target);
}
}
}, 500));
observer.observe(document.body, { childList: true, subtree: true, characterData: true });
document.querySelectorAll('*').forEach(el => {
for (const node of el.childNodes) {
convertTextToLinks(node);
}
});
})();