// ==UserScript== // @name 脚本猫去广告 // @namespace https://scriptcat.org/ // @version 1.1.0 // @description 隐藏 scriptcat.org 全站广告位,并移除 Google 自动广告(右下角购物胶囊 / 插屏 vignette / 锚定横幅)。无按钮无面板,装上即生效。 // @match *://scriptcat.org/* // @match *://*.scriptcat.org/* // @run-at document-start // @grant none // @license MIT // ==/UserScript== (function () { 'use strict'; // ── 第一层:站方广告位 ── // 全站广告位(横幅/侧栏/竖栏/卡片)统一渲染在 [data-ad-render] 容器里, // 内含"广告"标签 + ins.adsbygoogle。用 CSS 按属性隐藏而不是 remove: // React 重渲染不会报错,document-start 注入也不会闪现。 const css = ` [data-ad-render] { display: none !important; } ins.adsbygoogle { display: none !important; height: 0 !important; } `; const style = document.createElement('style'); style.textContent = css; (document.head || document.documentElement).appendChild(style); // ── 第二层:Google 自动广告 ── // 不经过站方广告位,由 Google 直接注入 / 根节点(如右下角 // 购物锚点 #google-anno-sa、插屏 vignette),且带全套内联 !important // 样式反 CSS 屏蔽,只能 JS 移除。这些元素不归 React 管,可安全删除; // 站方自己的广告位一定带 [data-ad-render] 包裹,一律跳过。 const AUTO_ID_RE = /^google[-_]?(anno|vignette|anchor)/i; const AUTO_SEL = [ 'ins.adsbygoogle', '[id^="google-anno"]', '[id^="google_vignette"]', '[id^="google-vignette"]', '[google-anchor-overlappable]', '[google-side-rail-overlap]', ].join(','); const AD_IFRAME_SEL = 'iframe[src*="googlesyndication.com/pagead/ads"], iframe[src*="doubleclick.net/pagead/ads"]'; const sweep = (root) => { if (!(root instanceof Element)) return; const hit = AUTO_ID_RE.test(root.id || '') || root.hasAttribute('google-anchor-overlappable') || root.hasAttribute('google-side-rail-overlap') || (root.matches('ins.adsbygoogle') && !root.closest('[data-ad-render]')); if (hit) { root.remove(); return; } for (const el of root.querySelectorAll(AUTO_SEL)) { if (el.matches('ins.adsbygoogle') && el.closest('[data-ad-render]')) continue; el.remove(); } // 插屏广告等未知包装:顺着广告 iframe 往上找到 / 的直接 // 子节点整块移除;站方广告位里的 iframe 已被第一层覆盖,跳过。 for (const f of root.querySelectorAll(AD_IFRAME_SEL)) { if (f.closest('ins.adsbygoogle, [data-ad-render]')) continue; let t = f; while (t.parentElement && t.parentElement !== document.documentElement && t.parentElement !== document.body) { t = t.parentElement; } t.remove(); } }; new MutationObserver((muts) => { for (const m of muts) { for (const n of m.addedNodes) sweep(n); } }).observe(document.documentElement, { childList: true, subtree: true }); sweep(document.documentElement); })();