// ==UserScript==
// @name 隐藏任意元素
// @namespace https://github.com/giveme0101
// @version 2.4.1
// @description 通过右键点击隐藏页面上的任意元素,并保存隐藏状态,最新隐藏的显示在顶部
// @author Kevin [email protected]
// @match *://*/*
// @grant GM_info
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_registerMenuCommand
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 添加自定义样式
GM_addStyle(`
#element-hider-manage-btn {
position: fixed;
top: -6px;
right: -6px;
z-index: 9999999999;
padding: 10px;
background: linear-gradient(45deg, #ff0000, #ff8000, #ffff00, #00ff00, #0080ff, #0000ff, #8000ff);
opacity: 0.3;
color: white;
border: none;
border-radius: 50%;
cursor: pointer;
font-size: 14px;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
font-family: Arial, sans-serif;
transition: all 0.3s ease;
}
#element-hider-manage-btn:hover {
background: linear-gradient(50deg, #ff0000, #ff8000, #ffff00, #00ff00, #0080ff, #0000ff, #8000ff);
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
transform: scale(1.5);
opacity: 0.9;
}
#element-hider-panel {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
border: 2px solid #333;
border-radius: 8px;
padding: 20px;
z-index: 9999999998;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
min-width: 500px;
max-width: 80%;
max-height: 80vh;
overflow-y: auto;
font-family: Arial, sans-serif;
}
#element-hider-panel h3 {
margin-top: 0;
color: #4a6bdf;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
#element-hider-panel ul {
padding-left: 0;
max-height: 300px;
overflow-y: auto;
margin: 15px 0;
}
#element-hider-panel li {
margin-bottom: 8px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px;
border-radius: 4px;
background: #f9f9f9;
transition: background 0.2s;
}
#element-hider-panel li:hover {
background: #f0f0f0;
}
#element-hider-panel li .selector-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 80%;
font-family: monospace;
font-size: 12px;
}
#element-hider-panel .button-group {
display: flex;
gap: 5px;
}
#element-hider-panel button {
background: #4a6bdf;
color: white;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
transition: background 0.2s;
}
#element-hider-panel button:hover {
background: #3a5bc0;
}
#element-hider-panel .preview-btn {
background: #ffa500;
}
#element-hider-panel .preview-btn:hover {
background: #e59400;
}
#element-hider-panel .restore-btn {
background: #4CAF50;
}
#element-hider-panel .restore-btn:hover {
background: #3e8e41;
}
#element-hider-panel .clear-btn {
background: #ff4757;
padding: 8px 12px;
}
#element-hider-panel .clear-btn:hover {
background: #e03e4d;
}
#element-hider-panel .start-btn {
background: #6c757d;
padding: 8px 12px;
}
#element-hider-panel .start-btn:hover {
background: #5a6268;
}
.element-hider-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
background: rgba(0,0,0,0.5);
animation: fadeIn 0.3s ease;
}
.element-hider-tooltip {
position: fixed;
background: #333;
color: white;
padding: 8px 12px;
border-radius: 4px;
z-index: 10000;
font-size: 12px;
pointer-events: none;
font-family: Arial, sans-serif;
animation: fadeIn 0.2s ease;
max-width: 300px;
word-break: break-word;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.element-highlight {
outline: 2px solid red !important;
outline-offset: 2px;
position: relative;
z-index: 9998;
}
.element-highlight::after {
content: '隐藏元素预览';
position: absolute;
top: -25px;
left: 0;
background: red;
color: white;
padding: 2px 5px;
border-radius: 3px;
font-size: 10px;
z-index: 9999;
}
`);
// 存储隐藏元素的数据结构
const STORAGE_KEY = 'hidden_elements';
const STATE_KEY = 'state_key'
;
let hiddenElements = JSON.parse(GM_getValue(STORAGE_KEY, '{}'));
// 初始化当前页面的隐藏记录
const currentUrl = window.location.host + window.location.pathname;
if (!hiddenElements[currentUrl]) {
hiddenElements[currentUrl] = [];
}
// 应用已保存的隐藏元素
function applyHiddenElements() {
// 从最新的开始应用(后隐藏的先应用)
const elementsToHide = [...hiddenElements[currentUrl]].reverse();
elementsToHide.forEach(selector => {
try {
const elements = document.querySelectorAll(selector);
elements.forEach(element => {
element.style.display = 'none';
element.setAttribute('data-hidden-by-element-hider', 'true');
});
} catch (e) {
console.warn('无法隐藏元素:', selector, e);
}
});
}
// 查找最内层的元素
function findInnermostElement(x, y) {
return document.elementFromPoint(x, y);
}
// 生成CSS选择器
function generateSelector(element) {
// 如果有ID,优先使用ID选择器
if (element.id) {
return `#${CSS.escape(element.id)}`;
}
let selector = element.tagName.toLowerCase();
// 添加类名
if (element.className && typeof element.className === 'string') {
const classes = element.className.split(/\s+/).filter(cls => cls.length > 0);
if (classes.length > 0) {
selector += '.' + classes.map(cls => CSS.escape(cls)).join('.');
}
}
// 如果没有类名和ID,添加属性选择器
if (!element.id && (!element.className || element.className.trim() === '')) {
// 尝试使用其他属性
const attrs = ['name', 'type', 'alt', 'title', 'src', 'href'];
for (const attr of attrs) {
if (element.hasAttribute(attr)) {
const value = element.getAttribute(attr);
if (value && value.length < 50) { // 避免过长的值
selector += `[${attr}="${CSS.escape(value)}"]`;
break;
}
}
}
}
// 添加父元素信息提高特异性
let path = [];
let current = element;
let limit = 0;
while (current.parentElement && limit < 5) {
const parent = current.parentElement;
let parentSelector = parent.tagName.toLowerCase();
if (parent.id) {
parentSelector = `#${CSS.escape(parent.id)}`;
path.unshift(parentSelector);
break;
} else if (parent.className && typeof parent.className === 'string') {
const classes = parent.className.split(/\s+/).filter(cls => cls.length > 0);
if (classes.length > 0) {
parentSelector += '.' + classes.map(cls => CSS.escape(cls)).join('.');
}
}
path.unshift(parentSelector);
current = parent;
limit++;
}
if (path.length > 0) {
selector = path.join(' > ') + ' > ' + selector;
}
// 添加:nth-child信息
if (element.parentElement) {
const children = Array.from(element.parentElement.children);
const index = children.indexOf(element);
if (index >= 0) {
selector += `:nth-child(${index + 1})`;
}
}
return selector;
}
// 隐藏元素并保存
function hideElement(element) {
const selector = generateSelector(element);
element.style.display = 'none';
element.setAttribute('data-hidden-by-element-hider', 'true');
// 检查是否已存在相同的选择器
const existingIndex = hiddenElements[currentUrl].indexOf(selector);
if (existingIndex > -1) {
// 如果已存在,先移除旧的
hiddenElements[currentUrl].splice(existingIndex, 1);
}
// 将新的选择器添加到数组开头(最新隐藏的显示在最上面)
hiddenElements[currentUrl].unshift(selector);
GM_setValue(STORAGE_KEY, JSON.stringify(hiddenElements));
return selector;
}
// 显示管理界面
function showManagementPanel() {
// 如果面板已存在,先移除
const existingPanel = document.getElementById('element-hider-panel');
if (existingPanel) {
existingPanel.remove();
}
const existingOverlay = document.querySelector('.element-hider-overlay');
if (existingOverlay) {
existingOverlay.remove();
}
// 创建浮窗
const panel = document.createElement('div');
panel.id = 'element-hider-panel';
// 标题
const title = document.createElement('h3');
title.textContent = `隐藏元素管理 (${hiddenElements[currentUrl].length}) - ${currentUrl}`;
panel.appendChild(title);
// 当前页面隐藏元素列表
const list = document.createElement('ul');
if (hiddenElements[currentUrl].length === 0) {
const item = document.createElement('li');
item.textContent = '当前页面没有隐藏元素';
item.style.background = 'none';
item.style.justifyContent = 'center';
list.appendChild(item);
} else {
// 显示所有隐藏元素,最新的在最上面
hiddenElements[currentUrl].forEach((selector, index) => {
const item = document.createElement('li');
const text = document.createElement('span');
text.className = 'selector-text';
text.textContent = selector;
text.title = selector;
const buttonGroup = document.createElement('div');
buttonGroup.className = 'button-group';
// 预览按钮
const previewBtn = document.createElement('button');
previewBtn.className = 'preview-btn';
previewBtn.textContent = '预览';
previewBtn.title = '预览此元素位置';
previewBtn.onclick = function() {
try {
const elements = document.querySelectorAll(selector);
elements.forEach(element => {
const originalDisplay = element.style.display;
const originalOutline = element.style.outline;
const originalZIndex = element.style.zIndex;
// 临时显示并高亮元素
element.style.display = '';
element.style.outline = '';
element.style.zIndex = '9998';
element.classList.add('element-highlight');
// 滚动到元素位置
element.scrollIntoView({behavior: 'smooth', block: 'center'});
// 3秒后恢复
setTimeout(() => {
element.style.display = originalDisplay;
element.style.outline = originalOutline;
element.style.zIndex = originalZIndex;
element.classList.remove('element-highlight');
}, 3000);
});
} catch (e) {
console.error('预览元素时出错:', e);
}
};
// 恢复按钮
const restoreBtn = document.createElement('button');
restoreBtn.className = 'restore-btn';
restoreBtn.textContent = '恢复';
restoreBtn.title = '恢复显示此元素';
restoreBtn.onclick = function() {
try {
const elements = document.querySelectorAll(selector);
elements.forEach(element => {
element.style.display = '';
element.removeAttribute('data-hidden-by-element-hider');
});
// 从存储中移除
hiddenElements[currentUrl].splice(index, 1);
GM_setValue(STORAGE_KEY, JSON.stringify(hiddenElements));
// 刷新列表
panel.remove();
overlay.remove();
showManagementPanel();
} catch (e) {
console.error('恢复元素时出错:', e);
}
};
buttonGroup.appendChild(previewBtn);
buttonGroup.appendChild(restoreBtn);
item.appendChild(text);
item.appendChild(buttonGroup);
list.appendChild(item);
});
}
panel.appendChild(list);
// 操作按钮
const clearBtn = document.createElement('button');
clearBtn.className = 'clear-btn';
clearBtn.textContent = '清除所有';
clearBtn.onclick = function() {
if (confirm('确定要恢复当前页面的所有隐藏元素吗?')) {
// 恢复所有元素显示
hiddenElements[currentUrl].forEach(selector => {
try {
const elements = document.querySelectorAll(selector);
elements.forEach(element => {
element.style.display = '';
element.removeAttribute('data-hidden-by-element-hider');
});
} catch (e) {
console.error('恢复元素时出错:', e);
}
});
// 清除存储
hiddenElements[currentUrl] = [];
GM_setValue(STORAGE_KEY, JSON.stringify(hiddenElements));
// 关闭面板
panel.remove();
overlay.remove();
// 显示成功消息
showTooltip('已恢复所有隐藏元素', window.innerWidth / 2, window.innerHeight / 2);
}
};
const startBtn = document.createElement('button');
startBtn.className = 'start-btn';
startBtn.textContent = GM_getValue(STATE_KEY, '0') === '1' ? "已开启" : "已关闭";
startBtn.onclick = function(e) {
GM_setValue(STATE_KEY, GM_getValue(STATE_KEY, '0') === '0' ? '1' : '0');
regDbClickEvt(e);
panel.remove();
overlay.remove();
};
const btnContainer = document.createElement('div');
btnContainer.style.marginTop = '15px';
btnContainer.style.display = 'flex';
btnContainer.style.justifyContent = 'space-between';
btnContainer.appendChild(clearBtn);
btnContainer.appendChild(startBtn);
panel.appendChild(btnContainer);
// 添加到页面
document.body.appendChild(panel);
// 点击背景关闭
const overlay = document.createElement('div');
overlay.className = 'element-hider-overlay';
overlay.onclick = function() {
panel.remove();
overlay.remove();
};
document.body.appendChild(overlay);
}
// 显示提示信息
function showTooltip(message, x, y) {
const tooltip = document.createElement('div');
tooltip.className = 'element-hider-tooltip';
tooltip.textContent = message;
tooltip.style.top = `${y}px`;
tooltip.style.left = `${x}px`;
document.body.appendChild(tooltip);
// 2秒后消失
setTimeout(() => {
tooltip.remove();
}, 2000);
}
// 右键事件处理
function handleRightClick(e) {
// 如果点击的是管理面板本身或管理按钮,则不处理
if (e.target.closest('#element-hider-panel') || e.target.id === 'element-hider-manage-btn') {
return;
}
// 阻止默认右键菜单
e.preventDefault();
const element = findInnermostElement(e.clientX, e.clientY);
if (element && element !== document.documentElement && element !== document.body) {
const selector = hideElement(element);
// 显示提示信息
showTooltip(`已隐藏: ${element.tagName}${element.id ? '#' + element.id : ''}`, e.pageX, e.pageY - 30);
}
}
function regDbClickEvt(e){
const openStatus = GM_getValue(STATE_KEY, '0');
if (openStatus === '1') {
showTooltip('已开启右键点击隐藏功能', e.pageX, e.pageY - 30);
document.addEventListener('contextmenu', handleRightClick);
} else {
showTooltip('已关闭右键点击隐藏功能', e.pageX, e.pageY - 30);
document.removeEventListener('contextmenu', handleRightClick);
}
}
// 初始化
function init() {
// 应用已保存的隐藏元素
applyHiddenElements();
// 重置状态
GM_setValue(STATE_KEY, '0');
// 添加管理按钮到页面
const manageBtn = document.createElement('button');
manageBtn.id = 'element-hider-manage-btn';
// manageBtn.textContent = '.';
manageBtn.title = '隐藏元素管理';
manageBtn.onclick = showManagementPanel;
document.body.appendChild(manageBtn);
// 注册油猴菜单命令
if (typeof GM_registerMenuCommand !== 'undefined') {
GM_registerMenuCommand('管理隐藏元素', showManagementPanel);
}
}
// 页面加载完成后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();