见code

// ==UserScript==
// @name         Google 搜索结果域名修正(v2ex)
// @namespace    jimmy.plugin.v2ex-domain-fix
// @version      1.1
// @description  将 Google 搜索结果中 v2ex.com 的三级域名链接(如 www.v2ex.com、hk.v2ex.com)改写为可正常访问的二级域名
// @match        https://www.google.com/*
// @match        https://www.google.com.hk/*
// @run-at       document-idle
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  // 需要"三级域名 -> 二级域名"归一化处理的根域名列表。
  // 只有 v2ex.com 本身可访问,www.v2ex.com / hk.v2ex.com 等子域名均需改写为 v2ex.com。
  // 后续如需支持其他站点,直接往数组里追加根域名即可。
  const ROOT_DOMAINS = ['v2ex.com'];

  const DOMAIN_TEXT_RE = new RegExp(
    ROOT_DOMAINS.map((r) => '([a-z0-9-]+\\.)+' + r.replace('.', '\\.')).join('|'),
    'gi'
  );

  function matchRootDomain(hostname) {
    for (const root of ROOT_DOMAINS) {
      if (hostname === root) return null; // 已经是二级域名,无需处理
      if (hostname.toLowerCase().endsWith('.' + root)) return root;
    }
    return null;
  }

  // 计算改写后的 URL,命中返回新地址,未命中返回 null。
  function fixedHref(href) {
    let url;
    try {
      url = new URL(href, location.href);
    } catch {
      return null;
    }
    const root = matchRootDomain(url.hostname);
    if (!root) return null;
    url.hostname = root;
    return url.toString();
  }

  function fixLink(a) {
    const fixed = fixedHref(a.href);
    if (fixed) a.href = fixed;
  }

  // 逐文本节点替换展示用的域名文字,不依赖具体标签( 等),避免 Google 改版后失效,
  // 也不会像直接改 el.textContent 那样把子节点结构一并破坏掉。
  function fixTextNodesUnder(root) {
    const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
    let node;
    while ((node = walker.nextNode())) {
      if (DOMAIN_TEXT_RE.test(node.nodeValue)) {
        node.nodeValue = node.nodeValue.replace(DOMAIN_TEXT_RE, (m) => {
          for (const r of ROOT_DOMAINS) {
            if (m.toLowerCase().endsWith(r)) return r;
          }
          return m;
        });
      }
    }
  }

  function scan(root) {
    let fixedCount = 0;
    root.querySelectorAll('a[href]').forEach((a) => {
      const before = a.href;
      fixLink(a);
      if (a.href !== before) fixedCount++;
    });
    fixTextNodesUnder(root);
    if (fixedCount > 0) {
      console.log('[v2ex-domain-fix] 本次修正了', fixedCount, '个链接');
    }
  }

  scan(document.body);

  // Google 搜索结果会在滚动加载更多 / 切换 Tab 时动态插入新节点,用 MutationObserver 持续处理。
  const observer = new MutationObserver((mutations) => {
    for (const mutation of mutations) {
      mutation.addedNodes.forEach((node) => {
        if (node.nodeType === Node.ELEMENT_NODE) {
          scan(node);
        }
      });
    }
  });
  observer.observe(document.body, { childList: true, subtree: true });

  // 兜底:万一某个链接因渲染时机没被扫描到,点击瞬间也强制纠正跳转地址,
  // 用 capture 阶段保证在浏览器发起导航之前完成改写。
  document.addEventListener(
    'click',
    (e) => {
      const a = e.target instanceof Element ? e.target.closest('a[href]') : null;
      if (!a) return;
      const fixed = fixedHref(a.href);
      if (fixed && fixed !== a.href) {
        console.log('[v2ex-domain-fix] 点击兜底修正:', a.href, '->', fixed);
        a.href = fixed;
      }
    },
    true
  );
})();

标签: none

添加新评论