您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Hides the DAZN FanZone
// ==UserScript== // @name DAZN FanZone Remover // @namespace https://github.com/ImElio/dazn-fanzone-remover // @version 1.0.1 // @description Hides the DAZN FanZone // @author Elio & Shokkino // @license MIT // @homepageURL https://github.com/ImElio/dazn-fanzone-remover // @supportURL https://github.com/ImElio/dazn-fanzone-remover/issues // @match https://www.dazn.com/* // @run-at document-start // @grant none // ==/UserScript== (function () { 'use strict'; /** Set to true for verbose diagnosis in the console (no runtime impact when false). */ const DEBUG = false; /** Attribute used to mark already-hidden containers (idempotency). */ const HIDDEN_ATTR = 'data-fz-hidden'; /** * DEPRECATED (old, brittle literal match): * const FANZONE_RE = null; // not used (strict equality only) * * FIX: Accept both "FanZone" and "Fan Zone" (any case), because some UIs compose the label inside sentences. */ const FANZONE_RE = /\bfan\s?zone\b/i; /** * FIX: Secondary signal — chat input placeholders are localized but stable (“Inizia a scrivere…”, “Write a message…”). * This helps when the visible label isn't an isolated "FanZone" node. */ const INPUT_RE = /(scriv|write|message|messag)/i; const log = (...args) => { if (DEBUG) console.info('[DAZN FanZone Remover]', ...args); }; const isVisible = (el) => !!el && el.offsetParent !== null; /** * pickContainer(from) * * DEPRECATED (oversimplified; risk of blank screen): * // const container = from.closest('aside') || from.closest('div'); * // container.style.display = 'none'; * Reason: climbing blindly to 'div' could target a large layout wrapper that also hosts the <video> player. * * FIX: Prefer semantic sidebars (ASIDE / role="complementary") when they do NOT contain <video>. * Otherwise, climb a few levels and pick the first wrapper that does NOT include <video>. * This preserves the player layout, preventing black screens. */ function pickContainer(from) { if (!from) return null; // Prefer semantic wrappers (most UIs wrap sidebars as <aside> or role="complementary"). const prefer = from.closest('aside, [role="complementary"]'); if (prefer && !prefer.querySelector('video')) return prefer; // Climb conservatively; stop early at the first safe wrapper. let cur = from; for (let i = 0; i < 6 && cur; i++) { cur = cur.parentElement; if (!cur) break; if (!cur.querySelector('video')) return cur; } // Last resort (still avoid <body> and any node that includes <video>). const fallback = from.closest('div, section, nav'); if (fallback && !fallback.querySelector('video') && fallback !== document.body) return fallback; return null; } /** * hide(el) * * DEPRECATED (no idempotency; noisy console): * // el.style.display = 'none'; * // console.log('FanZone removed:', el); * * FIX: Apply display:none with an attribute marker to avoid repeated work/logs across re-renders. */ function hide(el) { if (!el || el.hasAttribute(HIDDEN_ATTR)) return; el.style.setProperty('display', 'none', 'important'); el.setAttribute(HIDDEN_ATTR, '1'); log('hidden:', el); } /** * sweep() * Single pass that: * A) finds explicit "FanZone" signals (text or aria-label), * B) uses chat input placeholder as a robust backup signal (localized). * * DEPRECATED (strict-only text match, misses embedded labels): * // if (node.textContent?.trim() === 'FanZone') { ... } * * FIX: Combine exact, regex, and aria-label; then pick a safe container via pickContainer(). */ function sweep() { try { document.querySelectorAll('*').forEach(node => { if (!isVisible(node)) return; const txt = (node.textContent || '').trim(); const aria = node.getAttribute ? (node.getAttribute('aria-label') || '') : ''; // DEPRECATED (strict-only): // if (txt === 'FanZone') { const c = node.closest('aside') || node.closest('div'); c.style.display = 'none'; } // FIX: tolerate different casings and placements + aria-label if (txt === 'FanZone' || aria === 'FanZone' || FANZONE_RE.test(txt)) { const container = pickContainer(node); if (container) hide(container); } }); document.querySelectorAll('input[placeholder], textarea[placeholder]').forEach(inp => { if (!isVisible(inp)) return; const ph = inp.getAttribute('placeholder') || ''; if (INPUT_RE.test(ph)) { const container = pickContainer(inp); if (container) hide(container); } }); } catch (err) { // NOTE: tolerate occasional DOM access errors without breaking the loop. if (DEBUG) console.warn('[DAZN FanZone Remover] sweep error:', err); } } /** * DEPRECATED (observer only; may miss virtualized updates): * // new MutationObserver(sweep).observe(document.body, { childList: true, subtree: true }); * * FIX: Combine MutationObserver (reactive) with a periodic sweep (proactive), * mirroring the original "observer + setInterval" resiliency but with safer targeting. */ const mo = new MutationObserver(() => sweep()); function start() { if (!document.body) { setTimeout(start, 50); return; } mo.observe(document.body, { childList: true, subtree: true }); sweep(); setInterval(sweep, 1500); } start(); })();