// ==UserScript==
// @name 高级自动滚动控制器
// @namespace http://tampermonkey.net/
// @version 1.5
// @description 新增显示/隐藏面板功能(快捷键Alt+R),新增最小化快捷键(Alt+M),优化面板样式,新增关闭弹窗功能
// @author yangwenren
// @match *://*/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// 全局变量
let isScrolling = false;
let scrollSpeed = 5;
let scrollDirection = 'down';
let bottomAction = 'jump';
let panelMinimized = false;
let panelHidden = false;
let isDragging = false;
let dragOffsetX = 0;
let dragOffsetY = 0;
let scrollInterval = null;
let rafId = null;
const MIN_SPEED = 1;
const MAX_SPEED = 40;
const SAFE_MARGIN = 15;
const FIXED_PANEL_HEIGHT = 330;
const MINIMIZED_HEIGHT = 45;
const PANEL_WIDTH = 280;
const MINIMIZED_WIDTH = 200;
const TOGGLE_HOTKEY = 'Alt+R'; // 显示/隐藏快捷键
const MINIMIZE_HOTKEY = 'Alt+M'; // 最小化快捷键
// 弹窗控制变量
let isClosePopupEnabled = false;
let popupCheckInterval = null;
const POPUP_CHECK_DELAY = 1000;
let popupFlags = {
login: true, // 登录弹窗标记
download: true, // 下载弹窗标记
recommend: true // 推荐内容弹窗标记
};
let popupObserver = null;
// 创建控制界面
function createControlPanel() {
const panel = document.createElement('div');
panel.id = 'scrollControlPanel';
panel.style.cssText = `
position: fixed;
bottom: ${SAFE_MARGIN}px;
right: ${SAFE_MARGIN}px;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.12);
z-index: 999999;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
width: ${PANEL_WIDTH}px;
height: ${FIXED_PANEL_HEIGHT}px;
user-select: none;
will-change: transform, width, opacity;
touch-action: none;
transform: translateZ(0);
transition: width 0.2s ease, box-shadow 0.2s ease, opacity 0.3s ease, padding-right 0.2s ease;
overflow: hidden;
padding-right: 0;
`;
// 标题栏
const titleBar = document.createElement('div');
titleBar.style.cssText = `
padding: 10px 18px;
display: flex;
justify-content: space-between;
align-items: center;
cursor: move;
border-top-left-radius: 12px;
border-top-right-radius: 12px;
transition: background-color 0.2s ease;
height: 45px;
box-sizing: border-box;
`;
titleBar.classList.add('drag-handle');
titleBar.addEventListener('mousedown', () => {
titleBar.style.opacity = '0.9';
});
titleBar.addEventListener('mouseup', () => {
titleBar.style.opacity = '1';
});
const title = document.createElement('h3');
title.textContent = '高级滚动控制器';
title.style.cssText = 'margin: 0; font-size: 15px; font-weight: 500; letter-spacing: 0.3px; transition: font-size 0.2s ease;';
// 按钮容器
const buttonContainer = document.createElement('div');
buttonContainer.style.display = 'flex';
buttonContainer.style.gap = '6px';
// 最小化按钮
const minBtn = document.createElement('button');
minBtn.textContent = '−';
minBtn.title = `最小化面板(快捷键:${MINIMIZE_HOTKEY})`;
minBtn.style.cssText = `
width: 26px;
height: 26px;
border: none;
background: transparent;
font-size: 20px;
cursor: pointer;
line-height: 1;
padding: 0;
border-radius: 50%;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
`;
minBtn.addEventListener('mouseover', () => {
minBtn.style.backgroundColor = 'rgba(0,0,0,0.08)';
});
minBtn.addEventListener('mouseout', () => {
minBtn.style.backgroundColor = 'transparent';
});
minBtn.addEventListener('click', () => toggleMinimize(panel));
// 隐藏面板按钮
const hideBtn = document.createElement('button');
hideBtn.textContent = '⇨';
hideBtn.title = `隐藏面板(快捷键:${TOGGLE_HOTKEY})`;
hideBtn.style.cssText = `
width: 26px;
height: 26px;
border: none;
background: transparent;
font-size: 16px;
cursor: pointer;
line-height: 1;
padding: 0;
border-radius: 50%;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
`;
hideBtn.addEventListener('mouseover', () => {
hideBtn.style.backgroundColor = 'rgba(0,0,0,0.08)';
});
hideBtn.addEventListener('mouseout', () => {
hideBtn.style.backgroundColor = 'transparent';
});
hideBtn.addEventListener('click', () => togglePanelVisibility(panel));
buttonContainer.append(minBtn, hideBtn);
titleBar.append(title, buttonContainer);
panel.appendChild(titleBar);
// 内容区域
const contentArea = document.createElement('div');
contentArea.id = 'panelContent';
contentArea.style.cssText = `
padding: 18px;
transition: display 0.2s ease;
height: calc(${FIXED_PANEL_HEIGHT}px - 45px);
box-sizing: border-box;
overflow: hidden;
`;
// 速度控制
const speedDiv = document.createElement('div');
speedDiv.style.cssText = `
margin-bottom: 15px;
display: flex;
flex-direction: column;
gap: 6px;
`;
const speedLabel = document.createElement('label');
speedLabel.textContent = `滚动速度: ${scrollSpeed}`;
speedLabel.id = 'speedLabel';
speedLabel.style.cssText = `
display: block;
font-size: 14px;
font-weight: 400;
`;
const speedSlider = document.createElement('input');
speedSlider.type = 'range';
speedSlider.min = MIN_SPEED;
speedSlider.max = MAX_SPEED;
speedSlider.value = scrollSpeed;
speedSlider.style.cssText = `
width: 100%;
height: 6px;
-webkit-appearance: none;
appearance: none;
border-radius: 3px;
outline: none;
`;
speedSlider.style.setProperty('-webkit-slider-thumb', `
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: #4CAF50;
cursor: pointer;
transition: all 0.2s ease;
`);
speedSlider.addEventListener('input', function() {
scrollSpeed = parseInt(this.value);
document.getElementById('speedLabel').textContent = `滚动速度: ${scrollSpeed}`;
if (isScrolling) updateScrollInterval();
});
speedDiv.append(speedLabel, speedSlider);
contentArea.appendChild(speedDiv);
// 方向控制
const directionDiv = document.createElement('div');
directionDiv.style.cssText = `
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 12px;
`;
const directionLabel = document.createElement('label');
directionLabel.textContent = '滚动方向:';
directionLabel.style.cssText = `
width: 85px;
font-size: 14px;
font-weight: 400;
`;
const directionSelect = document.createElement('select');
directionSelect.style.cssText = `
flex: 1;
padding: 7px 12px;
border-radius: 6px;
border: 1px solid;
font-size: 14px;
background-color: transparent;
cursor: pointer;
transition: all 0.2s ease;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 12px center;
background-size: 14px;
padding-right: 35px;
`;
directionSelect.addEventListener('mouseover', () => {
directionSelect.style.borderColor = 'rgba(0,0,0,0.2)';
});
directionSelect.addEventListener('mouseout', () => {
updateTheme();
});
directionSelect.innerHTML = `
`;
directionSelect.value = scrollDirection;
directionSelect.addEventListener('change', function() {
scrollDirection = this.value;
if (isScrolling) updateScrollInterval();
});
directionDiv.append(directionLabel, directionSelect);
contentArea.appendChild(directionDiv);
// 底部行为控制
const actionDiv = document.createElement('div');
actionDiv.style.cssText = `
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 12px;
`;
const actionLabel = document.createElement('label');
actionLabel.textContent = '底部行为:';
actionLabel.style.cssText = `
width: 85px;
font-size: 14px;
font-weight: 400;
`;
const actionSelect = document.createElement('select');
actionSelect.style.cssText = `
flex: 1;
padding: 7px 12px;
border-radius: 6px;
border: 1px solid;
font-size: 14px;
background-color: transparent;
cursor: pointer;
transition: all 0.2s ease;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 12px center;
background-size: 14px;
padding-right: 35px;
`;
actionSelect.addEventListener('mouseover', () => {
actionSelect.style.borderColor = 'rgba(0,0,0,0.2)';
});
actionSelect.addEventListener('mouseout', () => {
updateTheme();
});
actionSelect.innerHTML = `
`;
actionSelect.value = bottomAction;
actionSelect.addEventListener('change', function() {
bottomAction = this.value;
if (isScrolling) updateScrollInterval();
});
actionDiv.append(actionLabel, actionSelect);
contentArea.appendChild(actionDiv);
// 自动关闭弹窗控制项
const popupControlDiv = document.createElement('div');
popupControlDiv.style.cssText = `
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 12px;
`;
const popupLabel = document.createElement('label');
popupLabel.textContent = '自动关闭弹窗:';
popupLabel.style.cssText = `
width: 100px;
font-size: 14px;
font-weight: 400;
`;
const popupToggle = document.createElement('input');
popupToggle.type = 'checkbox';
popupToggle.id = 'popupCloseToggle';
popupToggle.style.cssText = `
width: 18px;
height: 18px;
cursor: pointer;
accent-color: #4CAF50;
`;
popupToggle.checked = isClosePopupEnabled;
popupToggle.addEventListener('change', function() {
isClosePopupEnabled = this.checked;
if (isClosePopupEnabled) {
startPopupHandling();
} else {
stopPopupHandling();
}
});
popupControlDiv.append(popupLabel, popupToggle);
contentArea.appendChild(popupControlDiv);
// 弹窗类型过滤
const popupFilterDiv = document.createElement('div');
popupFilterDiv.style.cssText = `
margin-bottom: 15px;
display: flex;
flex-wrap: wrap;
gap: 8px;
font-size: 13px;
`;
const createFilterCheckbox = (type, label) => {
const container = document.createElement('div');
container.style.display = 'flex';
container.style.alignItems = 'center';
container.style.gap = '4px';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = `filter-${type}`;
checkbox.checked = popupFlags[type];
checkbox.style.width = '12px';
checkbox.style.height = '14px';
checkbox.addEventListener('change', () => {
popupFlags[type] = checkbox.checked;
});
const lbl = document.createElement('label');
lbl.htmlFor = `filter-${type}`;
lbl.textContent = label;
lbl.style.cursor = 'pointer';
container.append(checkbox, lbl);
return container;
};
popupFilterDiv.append(
createFilterCheckbox('login', '登录弹窗'),
createFilterCheckbox('download', '下载弹窗'),
createFilterCheckbox('recommend', '推荐弹窗')
);
contentArea.appendChild(popupFilterDiv);
// 控制按钮
const buttonDiv = document.createElement('div');
buttonDiv.style.cssText = `
display: flex;
gap: 10px;
`;
const startBtn = document.createElement('button');
startBtn.id = 'startScrollBtn';
startBtn.textContent = '开始滚动';
startBtn.style.cssText = `
flex: 1;
padding: 8px 12px;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s ease;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
`;
startBtn.addEventListener('mouseover', () => {
startBtn.style.transform = 'translateY(-1px)';
startBtn.style.boxShadow = '0 4px 8px rgba(0,0,0,0.15)';
});
startBtn.addEventListener('mouseout', () => {
startBtn.style.transform = 'translateY(0)';
startBtn.style.boxShadow = '0 2px 5px rgba(0,0,0,0.1)';
});
const stopBtn = document.createElement('button');
stopBtn.id = 'stopScrollBtn';
stopBtn.textContent = '停止滚动';
stopBtn.style.cssText = `
flex: 1;
padding: 8px 12px;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s ease;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
opacity: 0.7;
`;
stopBtn.disabled = true;
stopBtn.addEventListener('mouseover', () => {
if (!stopBtn.disabled) {
stopBtn.style.transform = 'translateY(-1px)';
stopBtn.style.boxShadow = '0 4px 8px rgba(0,0,0,0.15)';
}
});
stopBtn.addEventListener('mouseout', () => {
if (!stopBtn.disabled) {
stopBtn.style.transform = 'translateY(0)';
stopBtn.style.boxShadow = '0 2px 5px rgba(0,0,0,0.1)';
}
});
startBtn.addEventListener('click', () => {
if (!isScrolling) {
startScrolling();
isScrolling = true;
startBtn.disabled = true;
stopBtn.disabled = false;
stopBtn.style.opacity = '1';
}
});
stopBtn.addEventListener('click', () => {
stopScrolling();
isScrolling = false;
startBtn.disabled = false;
stopBtn.disabled = true;
stopBtn.style.opacity = '0.7';
});
buttonDiv.append(startBtn, stopBtn);
contentArea.appendChild(buttonDiv);
panel.appendChild(contentArea);
document.body.appendChild(panel);
// 初始化主题
updateTheme();
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateTheme);
// 初始化拖动
initDragEvents(panel, titleBar);
// 窗口大小变化监听
window.addEventListener('resize', () => {
if (!panelHidden) checkBounds(panel);
});
// 初始化快捷键
initHotkeys(panel);
return panel;
}
// 切换面板显示/隐藏状态
function togglePanelVisibility(panel) {
panelHidden = !panelHidden;
if (panelHidden) {
panel.style.opacity = '0';
panel.style.pointerEvents = 'none';
const hideBtn = panel.querySelector('button:last-of-type');
if (hideBtn) hideBtn.textContent = '⇦';
} else {
panel.style.opacity = '1';
panel.style.pointerEvents = 'auto';
const hideBtn = panel.querySelector('button:last-of-type');
if (hideBtn) hideBtn.textContent = '⇨';
checkBounds(panel);
}
}
// 切换最小化状态
function toggleMinimize(panel) {
if (panelHidden) return;
const content = document.getElementById('panelContent');
const minBtn = panel.querySelector('button:first-of-type');
const title = panel.querySelector('h3');
const buttonContainer = panel.querySelector('.drag-handle > div');
panelMinimized = !panelMinimized;
if (panelMinimized) {
content.style.display = 'none';
panel.style.width = `${MINIMIZED_WIDTH}px`;
panel.style.height = `${MINIMIZED_HEIGHT}px`;
panel.style.paddingRight = '0';
minBtn.textContent = '+';
title.style.fontSize = '15px';
buttonContainer.style.gap = '1px';
} else {
content.style.display = 'block';
panel.style.width = `${PANEL_WIDTH}px`;
panel.style.height = `${FIXED_PANEL_HEIGHT}px`;
panel.style.paddingRight = '0';
minBtn.textContent = '−';
title.style.fontSize = '15px';
buttonContainer.style.gap = '6px';
}
checkBounds(panel);
}
// 初始化所有快捷键
function initHotkeys(panel) {
document.addEventListener('keydown', (e) => {
// 显示/隐藏面板:Alt+R
if (e.altKey && e.key.toLowerCase() === 'r') {
e.preventDefault();
togglePanelVisibility(panel);
}
// 最小化/展开面板:Alt+M
if (e.altKey && e.key.toLowerCase() === 'm') {
e.preventDefault();
toggleMinimize(panel);
}
});
console.log(`高级滚动控制器快捷键:`);
console.log(`- ${TOGGLE_HOTKEY}:显示/隐藏面板`);
console.log(`- ${MINIMIZE_HOTKEY}:最小化/展开面板`);
}
// 检查面板位置
function checkBounds(panel) {
const panelHeight = panelMinimized ? MINIMIZED_HEIGHT : FIXED_PANEL_HEIGHT;
const panelWidth = panelMinimized ? MINIMIZED_WIDTH : PANEL_WIDTH;
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const rect = panel.getBoundingClientRect();
let newLeft = rect.left;
let newTop = rect.top;
if (newLeft < SAFE_MARGIN) newLeft = SAFE_MARGIN;
else if (newLeft + panelWidth > viewportWidth - SAFE_MARGIN) {
newLeft = viewportWidth - panelWidth - SAFE_MARGIN;
}
if (newTop < SAFE_MARGIN) newTop = SAFE_MARGIN;
else if (newTop + panelHeight > viewportHeight - SAFE_MARGIN) {
newTop = viewportHeight - panelHeight - SAFE_MARGIN;
}
if (newLeft !== rect.left || newTop !== rect.top) {
panel.style.left = `${newLeft}px`;
panel.style.top = `${newTop}px`;
panel.style.right = 'auto';
panel.style.bottom = 'auto';
panel.style.transform = 'translateZ(0)';
}
}
// 拖动事件
function initDragEvents(panel, handle) {
let panelInitialRect;
handle.addEventListener('mousedown', (e) => {
if (panelHidden) return;
e.preventDefault();
e.stopPropagation();
isDragging = true;
panelInitialRect = panel.getBoundingClientRect();
dragOffsetX = e.clientX - panelInitialRect.left;
dragOffsetY = e.clientY - panelInitialRect.top;
document.body.style.cursor = 'grabbing';
handle.style.cursor = 'grabbing';
document.body.style.userSelect = 'none';
panel.style.boxShadow = '0 6px 25px rgba(0,0,0,0.15)';
});
document.addEventListener('mousemove', (e) => {
if (!isDragging || panelHidden) return;
e.preventDefault();
if (rafId) cancelAnimationFrame(rafId);
rafId = requestAnimationFrame(() => {
const newLeft = e.clientX - dragOffsetX;
const newTop = e.clientY - dragOffsetY;
panel.style.transform = `translate(
${newLeft - panelInitialRect.left}px,
${newTop - panelInitialRect.top}px
) translateZ(0)`;
});
});
function endDrag() {
if (isDragging) {
isDragging = false;
cancelAnimationFrame(rafId);
rafId = null;
document.body.style.cursor = '';
handle.style.cursor = 'move';
document.body.style.userSelect = '';
panel.style.boxShadow = '0 4px 20px rgba(0,0,0,0.12)';
const rect = panel.getBoundingClientRect();
panel.style.left = `${rect.left}px`;
panel.style.top = `${rect.top}px`;
panel.style.transform = 'translateZ(0)';
checkBounds(panel);
}
}
document.addEventListener('mouseup', endDrag);
document.addEventListener('mouseleave', endDrag);
}
// 停止滚动函数
function stopScrolling() {
if (isScrolling && scrollInterval) {
clearInterval(scrollInterval);
scrollInterval = null;
}
isScrolling = false;
const startBtn = document.getElementById('startScrollBtn');
const stopBtn = document.getElementById('stopScrollBtn');
if (startBtn) startBtn.disabled = false;
if (stopBtn) {
stopBtn.disabled = true;
stopBtn.style.opacity = '0.7';
}
}
// 主题更新函数
function updateTheme() {
const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
const panel = document.getElementById('scrollControlPanel');
if (!panel) return;
const titleBar = panel.querySelector('.drag-handle');
const title = panel.querySelector('h3');
const buttons = panel.querySelectorAll('.drag-handle button');
const labels = panel.querySelectorAll('label');
const selects = panel.querySelectorAll('select');
const startBtn = document.getElementById('startScrollBtn');
const stopBtn = document.getElementById('stopScrollBtn');
const speedSlider = panel.querySelector('input[type="range"]');
if (isDarkMode) {
panel.style.backgroundColor = 'rgba(28, 28, 30, 0.98)';
panel.style.border = '1px solid rgba(70, 70, 75, 0.5)';
titleBar.style.backgroundColor = 'rgba(44, 44, 46, 0.9)';
title.style.color = '#f5f5f7';
buttons.forEach(btn => btn.style.color = '#d2d2d7');
labels.forEach(label => label.style.color = '#e4e4e7');
selects.forEach(select => {
select.style.backgroundColor = 'rgba(44, 44, 46, 0.8)';
select.style.color = '#e4e4e7';
select.style.borderColor = 'rgba(70, 70, 75, 0.8)';
});
speedSlider.style.backgroundColor = 'rgba(70, 70, 75, 0.5)';
speedSlider.style.setProperty('-webkit-slider-thumb', `
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: #4CAF50;
cursor: pointer;
transition: all 0.2s ease;
`);
if (startBtn) startBtn.style.backgroundColor = '#43a047';
if (stopBtn) stopBtn.style.backgroundColor = '#e53935';
} else {
panel.style.backgroundColor = 'rgba(255, 255, 255, 0.98)';
panel.style.border = '1px solid rgba(220, 220, 225, 0.8)';
titleBar.style.backgroundColor = 'rgba(249, 249, 250, 0.9)';
title.style.color = '#1d1d1f';
buttons.forEach(btn => btn.style.color = '#6e6e73');
labels.forEach(label => label.style.color = '#1d1d1f');
selects.forEach(select => {
select.style.backgroundColor = 'rgba(249, 249, 250, 0.8)';
select.style.color = '#1d1d1f';
select.style.borderColor = 'rgba(220, 220, 225, 0.8)';
});
speedSlider.style.backgroundColor = 'rgba(220, 220, 225, 0.8)';
speedSlider.style.setProperty('-webkit-slider-thumb', `
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: #4CAF50;
cursor: pointer;
transition: all 0.2s ease;
`);
if (startBtn) startBtn.style.backgroundColor = '#4CAF50';
if (stopBtn) stopBtn.style.backgroundColor = '#f44336';
}
}
// 更新滚动间隔
function updateScrollInterval() {
if (scrollInterval) {
clearInterval(scrollInterval);
scrollInterval = null;
}
startScrolling();
}
// 滚动逻辑实现
function startScrolling() {
if (scrollInterval) return;
scrollInterval = setInterval(() => {
const currentPos = window.scrollY;
const windowHeight = window.innerHeight;
const docHeight = Math.max(
document.body.scrollHeight,
document.body.offsetHeight,
document.documentElement.scrollHeight,
document.documentElement.offsetHeight
);
const atBottom = currentPos + windowHeight >= docHeight - 50;
const atTop = currentPos <= 50;
let newPos = currentPos;
if (scrollDirection === 'down') {
newPos = currentPos + scrollSpeed;
if (atBottom) {
switch (bottomAction) {
case 'jump':
newPos = 0;
break;
case 'reverse':
scrollDirection = 'up';
newPos = currentPos - scrollSpeed;
break;
case 'stop':
stopScrolling();
return;
}
}
} else {
newPos = currentPos - scrollSpeed;
if (atTop) {
switch (bottomAction) {
case 'jump':
newPos = docHeight - windowHeight;
break;
case 'reverse':
scrollDirection = 'down';
newPos = currentPos + scrollSpeed;
break;
case 'stop':
stopScrolling();
return;
}
}
}
window.scrollTo(0, newPos);
}, 20);
}
// 弹窗选择器集合,覆盖更多场景
const popupSelectors = {
login: [
".passport-login-container", ".signFlowModal", ".login-popover",
".modal-dialog-centered", ".loginBenefitNotification", ".lt-row",
".login-panel-popover:has(.login-tip-content)", ".bpx-player-toast-wrap",
"div:has(.unlogin-popover-avatar)", ".vW4dJNzI", "#related-video-card-login-guide",
".HDhMLx9a", ".modal__login", ".sideUnlogin",
".ReactModal__Overlay--after-open:not(.ReactModal__Overlay_content-page)"
],
download: [
".weixin-shadowbox", ".download-app-guidance", ".app-open-drawer",
".at-app-banner", ".callEnd_box", ".m-iqyGuide-layer",
".m-open-app.fixed-openapp", ".m-space-float-openapp", ".mplayer-widescreen-callapp",
".m-float-openapp", ".openapp-dialog", ".xigua-download",
".album-btn-container", ".btn-open", ".downloadButton", ".app-fixed-btn",
".app-download__wrapper", ".FloatDownloadButton_mobile_xiaoxue-button_1jZ",
".index_xiaoxue-button_1Av", ".widget__download-app", ".app-opener"
],
recommend: [
".recommend-box", ".add-panel", "#dftt-message-wrapper", ".trial-feed-wrap",
".feed-Sign-weixin", ".feed-Sign-span", ".call-app-btn", ".index_call-app-btn",
".open-button", ".bottom-login-guide", "#J-invoke-baiduApp-float", ".nav-bar-bottom",
".wk-student-defense", ".wk-student-limit-jump", ".bartop", ".wk-bottom-btn",
"#bdrainrwDragButton", ".drag-bottom", ".slider-top-bar_sliderWrapper__1Nize",
".bottom-bar_buttonWrap__NXBe-", "#opeApp", ".undefined", ".Corner-container",
".open-app-top", ".top-video-card-img-app", ".video-player-download-tips",
".open-app-bottom", ".NewOpenApp", ".player-lefttip-inner", "#passport-login-pop",
".pop-mask", ".page-top-rightinfo-popover", "#popupModule", ".open-link",
"[srcid=xcx_multi]", "[srcid=app_mobile_simple]", "[srcid=app_mobile_simple_safety]",
"[srcid=app_mobile_ios]"
]
};
// 关闭按钮选择器集合
const closeButtonSelectors = [
".close", ".Modal-closeButton", ".bili-mini-close-icon",
".DKE9HSAk", "img[alt='关闭']", "[class*='close']", "[class*='Close']",
".icon-btn-wrapper", ".ant-modal-close", "button:contains('关闭')",
"button:contains('取消')", "span:contains('×')", "button:contains('×')"
];
// 通过内容(xpath)获取节点
function getXpath(xpath, parent) {
try {
let xpathResult = document.evaluate(xpath, parent || document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
return xpathResult.singleNodeValue;
} catch (e) {
return null;
}
}
// 安全点击函数,避免触发防点击劫持
function safeClick(button) {
try {
// 先触发鼠标按下事件
const mousedownEvent = new MouseEvent('mousedown', {
bubbles: true,
cancelable: true,
view: window
});
button.dispatchEvent(mousedownEvent);
// 再触发点击事件
const clickEvent = new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window
});
button.dispatchEvent(clickEvent);
// 最后触发鼠标释放事件
const mouseupEvent = new MouseEvent('mouseup', {
bubbles: true,
cancelable: true,
view: window
});
button.dispatchEvent(mouseupEvent);
} catch (e) {
console.error('点击关闭按钮失败:', e);
}
}
// 智能查找关闭按钮
function findCloseButton(popup) {
// 先尝试直接选择器查找
for (const selector of closeButtonSelectors) {
try {
const btn = popup.querySelector(selector);
if (btn) return btn;
} catch (e) {
continue;
}
}
// 再尝试通过文本查找
const textBasedButtons = [
getXpath("//button[contains(text(), '关闭')]", popup),
getXpath("//a[contains(text(), '关闭')]", popup),
getXpath("//span[contains(text(), '关闭')]", popup)
];
return textBasedButtons.find(btn => btn !== null);
}
// 处理检测到的弹窗
function handlePopup(popup, type) {
// 尝试找到关闭按钮
const closeBtn = findCloseButton(popup);
if (closeBtn) {
safeClick(closeBtn);
console.log(`自动关闭${type}弹窗`);
return true;
} else if (type !== 'login') {
// 找不到关闭按钮时直接隐藏(登录弹窗除外)
popup.style.display = 'none !important';
console.log(`直接隐藏${type}弹窗`);
return true;
}
return false;
}
// 检测并关闭弹窗
function detectAndClosePopups() {
// 检查所有新增节点
const allElements = document.getElementsByTagName('*');
for (let element of allElements) {
// 检测各类弹窗
Object.entries(popupSelectors).forEach(([type, selectors]) => {
if (!popupFlags[type]) return;
selectors.some(selector => {
try {
if (element.matches(selector)) {
return handlePopup(element, type);
}
const popup = element.querySelector(selector);
if (popup) {
return handlePopup(popup, type);
}
} catch (e) {
return false;
}
return false;
});
});
}
}
// 启动弹窗处理
function startPopupHandling() {
// 停止任何已有的处理
stopPopupHandling();
// 立即执行一次检测
detectAndClosePopups();
// 设置定时检测作为备份
popupCheckInterval = setInterval(detectAndClosePopups, POPUP_CHECK_DELAY);
// 创建DOM变化观察者
popupObserver = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
for (const node of mutation.addedNodes) {
if (!(node instanceof HTMLElement)) continue;
// 检测并处理各类弹窗
Object.entries(popupSelectors).forEach(([type, selectors]) => {
if (!popupFlags[type]) return;
selectors.some(selector => {
try {
const popup = node.querySelector(selector) ||
(node.matches(selector) ? node : null);
if (popup) {
return handlePopup(popup, type);
}
} catch (e) {
return false;
}
return false;
});
});
}
}
});
// 启动观察者
popupObserver.observe(document, {
childList: true,
subtree: true,
attributes: false,
characterData: false
});
}
// 停止弹窗处理
function stopPopupHandling() {
if (popupCheckInterval) {
clearInterval(popupCheckInterval);
popupCheckInterval = null;
}
if (popupObserver) {
popupObserver.disconnect();
popupObserver = null;
}
}
// 初始化
if (document.readyState === 'complete' || document.readyState === 'interactive') {
createControlPanel();
} else {
document.addEventListener('DOMContentLoaded', createControlPanel);
setTimeout(createControlPanel, 3000);
}
})();