Weibo.cn/pub 关键词白字(WAP稳妥版)

在 weibo.cn(含 /pub)把指定关键词渲染为白色;适配早期WAP结构,附带轮询确保生效

当前为 2025-09-22 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Weibo.cn/pub 关键词白字(WAP稳妥版)
// @namespace    local.weibo.kwwhite
// @version      1.0.0
// @description  在 weibo.cn(含 /pub)把指定关键词渲染为白色;适配早期WAP结构,附带轮询确保生效
// @author       you
// @match        *://*.weibo.cn/*
// @match        *://weibo.cn/*
// @match        *://rebang.today/*
// @run-at       document-end
// @grant        none
// @license MIT
// ==/UserScript==

(function () {
  'use strict';

  /** ① 在这里添加/删除你的关键词 **/
  const KEYWORDS = ["日本","中国","乌克兰","以色列","巴勒斯坦","加沙","北京","上海","广东","深圳","广州","南京","杭州","新疆","柬埔寨","网红","警方","官方","官宣","通报","美学","建议","如何","看待","知道","终于","为啥","回应","怎么","评论","评价","是不是","偷偷","啥样","体制内","原生家庭","王安宇","沈月","田栩宁","严浩翔","孙怡","董子健","韩庚","卢靖姗","文章","马伊琍","胡兵","瞿颖","谢霆锋","金晨","黄圣依","檀健次","蔡徐坤", "王一博", "肖战","王俊凯", "王源", "易烊千玺","贾玲", "黄磊", "脱口秀","黄子韬", "沈腾", "赵丽颖","迪丽热巴","热巴","周杰伦","赵露思","邓超","鹿晗","陈赫","王鹤棣","虞书欣","白鹿","小米","华为","鞠婧炜","五月天","中医","中药","雷军","卢伟冰","罗永浩","余承东","陈乔恩","岳云鹏","郭德纲","王楚钦","孙颖莎","张艺兴","时代少年团","iPhone","黄晓明","angelababy","张天爱","吴彦祖","王力宏","韩安冉","李乃文","王冕","服务员","临时工","后续","向佐","向太","张家辉","周星驰","马柏全","张晚意","陈紫函","戴向宇","幽门螺旋杆菌","葛夕","白敬亭","辛芷蕾","黄灿灿","李荣浩","杨丞琳","高圆圆","赵又廷","关晓彤","刘涛","李沁","薛之谦","李一桐","淘宝","美团","马思纯","于正","王晶","王菲","窦靖童","周冬雨","杨蓉","章若楠","台风","航母","唐嫣","白百何"];

  /** ② 可选:是否不区分大小写(中文一般无所谓) **/
  const CASE_INSENSITIVE = true;

  /** ③ 轮询间隔(毫秒)。为兼容 /pub 的局部刷新,建议保留 **/
  const POLL_MS = 1000;

  // --- 正则 ---
  const regex = buildRegex(KEYWORDS, CASE_INSENSITIVE);
  if (!regex) return;

  // 初次执行 + 轮询
  runOnce();
  setInterval(runOnce, POLL_MS);

  function runOnce() {
    // 老页尽量少过滤,直接全页扫;但跳过常见不可见容器
    highlightIn(document.body);
  }

  function buildRegex(words, ci) {
    const parts = (words || []).filter(Boolean).map(escapeRegExp);
    if (!parts.length) return null;
    return new RegExp("(" + parts.join("|") + ")", ci ? "gi" : "g");
  }
  function escapeRegExp(s) {
    return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  }

  // 核心:扫描文本节点,把命中片段用 <span style="color:#fff !important" data-kw-hit> 包裹
  function highlightIn(root) {
    if (!root || !regex) return;

    const ign = node => {
      if (node.nodeType !== 1) return false;
      const tag = node.tagName;
      // 最小化忽略清单:脚本/样式/内嵌对象
      return tag === "SCRIPT" || tag === "STYLE" || tag === "IFRAME" || tag === "OBJECT" || tag === "NOSCRIPT";
    };
    if (ign(root)) return;

    const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);
    const nodes = [];
    let n;
    while ((n = walker.nextNode())) nodes.push(n);

    for (const textNode of nodes) {
      const parent = textNode.parentNode;
      if (!parent) continue;
      // 如果父节点已经是我们包裹过的片段,跳过
      if (parent.nodeType === 1 && parent.hasAttribute && parent.hasAttribute("data-kw-hit")) continue;

      const text = textNode.nodeValue;
      if (!text || !regex.test(text)) continue;
      regex.lastIndex = 0;

      const frag = document.createDocumentFragment();
      let last = 0;
      text.replace(regex, (m, _g1, idx) => {
        // 前段原样
        if (idx > last) frag.appendChild(document.createTextNode(text.slice(last, idx)));
        // 命中片段 -> 白字
        const span = document.createElement("span");
        span.setAttribute("style", "color:#fff !important;");
        span.setAttribute("data-kw-hit", "1");
        span.textContent = m;
        frag.appendChild(span);
        last = idx + m.length;
        return m;
      });
      // 末尾残余
      if (last < text.length) frag.appendChild(document.createTextNode(text.slice(last)));

      parent.replaceChild(frag, textNode);
    }
  }
})();