Google Exclusions Dynamic Clean

Blocks keywords and websites from Google search.

目前為 2025-11-22 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Google Exclusions Dynamic Clean
// @namespace    http://tampermonkey.net/
// @version      1.6
// @description  Blocks keywords and websites from Google search.
// @author       ScriptKing
// @match        https://www.google.com/search*
// @match        https://www.google.com/
// @grant        none
// @license MIT
// ==/UserScript==
(function() {
    'use strict';
    // =========================
    // CONFIG: Your exclusion list
    // =========================
    
    // IMPORTANT 
    // (1) update your website search engine to exclude the same keywords below
    // like this: https://www.google.com/search?q=%s+-site:website.com+-keyword
    // (2) put the exclusions also below
    const exclusions = ['website.com', 'keyword']; // 
    
    // =========================
    // HELPER FUNCTIONS
    // =========================
    // Wait for element with polling
    function waitForElement(selector, timeout = 5000, interval = 100) {
        return new Promise((resolve, reject) => {
            const start = Date.now();
            const check = () => {
                const el = document.querySelector(selector);
                if (el) {
                    resolve(el);
                } else if (Date.now() - start > timeout) {
                    reject(new Error('Element not found'));
                } else {
                    setTimeout(check, interval);
                }
            };
            check();
        });
    }
    
    // Check if URL already has exclusions
    function urlHasExclusions() {
        const urlParams = new URLSearchParams(window.location.search);
        const query = urlParams.get('q') || '';
        return exclusions.some(ex => query.toLowerCase().includes(`-${ex.toLowerCase()}`));
    }
    
    // Append exclusions if not already present
    function appendExclusions(query) {
        let newQuery = query;
        exclusions.forEach(ex => {
            const exStr = `-${ex}`;
            if (!newQuery.toLowerCase().includes(exStr.toLowerCase())) {
                newQuery += ` ${exStr}`;
            }
        });
        return newQuery;
    }
    
    // Clean query box by removing exclusion terms
    function cleanQueryBox() {
        // Only clean if we're on a search results page that came from browser search
        if (!window.location.pathname.includes('/search') || !urlHasExclusions()) {
            return; // Don't clean on homepage or if URL doesn't have exclusions
        }
        
        const selectors = 'input[name="q"], textarea[name="q"], input[type="search"], textarea';
        const queryBox = document.querySelector(selectors);
        
        if (queryBox && queryBox.value) {
            let cleaned = queryBox.value;
            exclusions.forEach(ex => {
                const exEsc = ex.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
                // Remove patterns like " -ex.com" or "-ex.com " with optional spaces
                const regex = new RegExp(`\\s*-${exEsc}\\s*`, 'gi');
                cleaned = cleaned.replace(regex, ' ');
            });
            // Normalize spaces
            cleaned = cleaned.replace(/\s+/g, ' ').trim();
            
            if (cleaned !== queryBox.value) {
                queryBox.value = cleaned;
                // Trigger input event for UI updates
                ['input', 'change'].forEach(eventType => {
                    queryBox.dispatchEvent(new Event(eventType, { bubbles: true }));
                });
            }
        }
    }
    
    // =========================
    // SEARCH BUTTON HANDLER
    // =========================
    async function setupSearchHandler() {
        try {
            // Wait for search button
            const button = await waitForElement('button[aria-label="Search"], input[name="btnK"], button[name="btnK"]');
            const queryBox = document.querySelector('input[name="q"], textarea[name="q"]') || button.closest('form')?.querySelector('input[name="q"], textarea');
            
            button.addEventListener('click', (e) => {
                if (queryBox && queryBox.value.trim()) {
                    e.preventDefault();
                    queryBox.value = appendExclusions(queryBox.value);
                    // Re-trigger submit after modification
                    setTimeout(() => {
                        if (queryBox.form) {
                            queryBox.form.submit();
                        }
                    }, 0);
                }
            }, { capture: true });
        } catch (err) {
            console.error('Search button not found:', err);
        }
    }
    
    // =========================
    // INITIALIZATION
    // =========================
    function initialize() {
        cleanQueryBox();
        setupSearchHandler();
    }
    
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }
    
    // Observe DOM changes for dynamic cleaning
    const observer = new MutationObserver(() => {
        cleanQueryBox();
    });
    
    // Start observing once body is available
    if (document.body) {
        observer.observe(document.body, { childList: true, subtree: true });
    }
    
    // Listen for Enter key
    document.addEventListener('keydown', (e) => {
        if (e.key === 'Enter' && e.target.matches('input[name="q"], textarea[name="q"]')) {
            const queryBox = e.target;
            if (queryBox.value.trim()) {
                queryBox.value = appendExclusions(queryBox.value);
            }
        }
    });
    
})();