您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
すべてのリンクをクリックできるリンクにかえる。
- // ==UserScript==
- // @name Convert Any Links to Clickable Links
- // @namespace kdroidwin.hatenablog.com
- // @version 3.1
- // @description すべてのリンクをクリックできるリンクにかえる。
- // @author Kdroidwin
- // @match *://*/*
- // @exclude *://github.com/*
- // @exclude *://chat.openai.com/*
- // @exclude *://blog.hatena.ne.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)));
- let url = match;
- if (url.startsWith('ttp')) {
- url = 'h' + url; // 'ttps://' → 'https://'
- } else if (!url.includes('://')) {
- url = 'https://' + url; // 'example.com' → 'https://example.com'
- }
- const a = document.createElement('a');
- a.href = url;
- a.textContent = url;
- a.target = '_blank';
- a.style.display = 'inline'; // レイアウト崩れ防止
- 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) {
- node.querySelectorAll('*').forEach(el => {
- for (const textNode of el.childNodes) {
- convertTextToLinks(textNode);
- }
- });
- } else if (node.nodeType === 3) {
- convertTextToLinks(node);
- }
- }
- } else if (mutation.type === 'characterData') {
- convertTextToLinks(mutation.target);
- }
- }
- }, 500));
- observer.observe(document.body, { childList: true, subtree: true, characterData: true });
- document.body.querySelectorAll('*').forEach(el => {
- for (const node of el.childNodes) {
- convertTextToLinks(node);
- }
- });
- })();