Blocks keywords and websites from Google search.
当前为
// ==UserScript==
// @name Google Exclusions Dynamic Clean
// @namespace http://tampermonkey.net/
// @version 1.5
// @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
// =========================
const exclusions = ['website1.com', 'website2.com', 'keyword1', 'keyword2']; // HERE
// =========================
// 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();
});
}
// Append exclusions if not already present
function appendExclusions(query) {
let newQuery = query;
exclusions.forEach(ex => {
const exStr = `-${ex}`;
if (!newQuery.includes(exStr)) {
newQuery += ` ${exStr}`;
}
});
return newQuery;
}
// Clean query box by removing exclusion terms
function cleanQueryBox() {
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 and search updates
['input', 'change', 'keyup'].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(); // Temporarily prevent to modify
queryBox.value = appendExclusions(queryBox.value);
// Re-trigger click or submit after modification
setTimeout(() => {
if (queryBox.form) {
queryBox.form.submit();
} else {
button.click();
}
}, 0);
}
}, { once: false }); // Allow multiple, but capture phase if needed
} catch (err) {
console.error('Search button not found:', err);
}
}
// =========================
// DYNAMIC CLEANING
// =========================
// Initial clean on load
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
cleanQueryBox();
setupSearchHandler();
});
} else {
cleanQueryBox();
setupSearchHandler();
}
window.addEventListener('load', () => {
cleanQueryBox();
setupSearchHandler();
});
// Observe DOM changes for dynamic cleaning and re-setup
const observer = new MutationObserver(() => {
cleanQueryBox();
// Re-setup if button lost (SPA navigation)
if (!document.querySelector('button[aria-label="Search"]')) {
setTimeout(setupSearchHandler, 500);
}
});
observer.observe(document.body, { childList: true, subtree: true });
// Also listen for Enter key as fallback
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);
// Let submit proceed
}
}
});
})();