您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
自动处理固定或粘性定位的顶部导航栏,根据滚动状态智能显示/隐藏,提升浏览体验
// ==UserScript== // @name F**k sticky header // @name:zh-CN 去你的固定顶栏 // @name:zh-TW 去你的固定頂欄 // @namespace fuck.sticky.header // @version 1.1 // @description Automatically handle sticky/fixed top headers, show/hide based on scroll // @description:zh-CN 自动处理固定或粘性定位的顶部导航栏,根据滚动状态智能显示/隐藏,提升浏览体验 // @description:zh-TW 自動處理固定或粘性定位的頂部導航欄,根據滾動狀態智能顯示/隱藏,提升瀏覽體驗 // @author You // @match *://*/* // @grant GM_registerMenuCommand // @grant GM_getValue // @grant GM_setValue // @license MIT // ==/UserScript== (function() { 'use strict'; // Configuration parameters const CONFIG = { scrollThreshold: 5, // Minimum scroll distance to trigger action topThreshold: 100, // Top area where header should always show transitionDuration: '0.3s', // Animation duration maxTopValue: 40 // Accept elements with top value up to this (pixels) }; // Get whitelist from storage let whitelist = GM_getValue('whitelist', []); // Check if current domain is whitelisted function isWhitelisted() { const currentDomain = window.location.hostname; return whitelist.some(domain => currentDomain.includes(domain)); } // Add current site to whitelist function addToWhitelist() { const currentDomain = window.location.hostname; if (!whitelist.includes(currentDomain)) { whitelist.push(currentDomain); GM_setValue('whitelist', whitelist); alert(`Added ${currentDomain} to whitelist`); window.location.reload(); } else { alert(`${currentDomain} is already in whitelist`); } } // Remove current site from whitelist function removeFromWhitelist() { const currentDomain = window.location.hostname; const index = whitelist.indexOf(currentDomain); if (index !== -1) { whitelist.splice(index, 1); GM_setValue('whitelist', whitelist); alert(`Removed ${currentDomain} from whitelist`); window.location.reload(); } else { alert(`${currentDomain} is not in whitelist`); } } // Register Tampermonkey menu commands GM_registerMenuCommand('Add current site to whitelist', addToWhitelist); GM_registerMenuCommand('Remove current site from whitelist', removeFromWhitelist); // Exit if site is whitelisted if (isWhitelisted()) { return; } // Helper function to parse pixel values function parsePixelValue(value) { if (value.endsWith('px')) { return parseFloat(value); } return 0; } // Detect eligible header elements function detectHeaderElements() { // Collect all potential header elements const candidates = new Set(); // 1. Add <header> tags const headerTags = document.getElementsByTagName('header'); if (headerTags.length > 0) { Array.from(headerTags).forEach(el => candidates.add(el)); } // 2. Add elements with relevant keywords const keywords = ['nav', 'banner', 'header']; const allElements = document.getElementsByTagName('*'); for (const el of allElements) { const className = typeof el.className === 'string' ? el.className : ''; const hasKeyword = keywords.some(keyword => (el.id && el.id.toLowerCase().includes(keyword)) || (className && className.toLowerCase().includes(keyword)) ); if (hasKeyword) { candidates.add(el); } } // 3. Find full-width elements (100vw) regardless of keywords for (const el of allElements) { const computed = window.getComputedStyle(el); const bodyWidth = window.getComputedStyle(document.body).width; if (['100vw', bodyWidth].includes(computed.width)) { candidates.add(el); } } // Filter to find valid top headers const validHeaders = []; for (const el of candidates) { // Check positioning const computed = window.getComputedStyle(el); const isStickyOrFixed = computed.position === 'sticky' || computed.position === 'fixed'; if (!isStickyOrFixed) continue; // Check top value (allow small values up to maxTopValue) const topValue = parsePixelValue(computed.top); if (topValue > CONFIG.maxTopValue) continue; // Check visual position and dimensions const rect = el.getBoundingClientRect(); const isWideEnough = rect.width >= window.innerWidth * 0.8 || computed.width === '100vw'; const isTallEnough = rect.height > 20; const isNearTop = rect.top <= CONFIG.topThreshold; if (isWideEnough && isTallEnough && isNearTop) { validHeaders.push(el); } } return validHeaders; } // Get all eligible headers const headerElements = detectHeaderElements(); if (headerElements.length === 0) { return; // No eligible headers found } // Add necessary styles function addStyles() { const styleSheet = document.createElement('style'); styleSheet.id = 'fuck-sticky-header-style'; // Generate unique selectors const selectors = headerElements.map(el => { if (el.id) return `#${CSS.escape(el.id)}`; return Array.from(el.classList).map(cls => `.${CSS.escape(cls)}`).join(', '); }).join(', '); styleSheet.textContent = ` ${selectors} { will-change: transform, opacity !important; transition: transform ${CONFIG.transitionDuration} cubic-bezier(0.4, 0, 0.2, 1), opacity ${CONFIG.transitionDuration} cubic-bezier(0.4, 0, 0.2, 1) !important; } html { overscroll-behavior-y: contain; } `; document.head.appendChild(styleSheet); } // Initialize variables let lastScrollTop = window.pageYOffset || document.documentElement.scrollTop; // Update header visibility function updateHeaders(shouldShow) { headerElements.forEach(el => { el.style.transform = shouldShow ? 'translateY(0)' : 'translateY(-100%)'; el.style.opacity = shouldShow ? '1' : '0'; }); } // Scroll handler function function handleScroll() { const scrollTop = window.pageYOffset || document.documentElement.scrollTop; // Handle Safari overscroll bounce const documentHeight = document.documentElement.scrollHeight; const viewportHeight = document.documentElement.clientHeight; const maxValidScrollTop = documentHeight - viewportHeight; if (scrollTop < 0 || scrollTop > maxValidScrollTop) return; // Calculate scroll difference const scrollDiff = scrollTop - lastScrollTop; // Only act on significant scroll movements if (Math.abs(scrollDiff) >= CONFIG.scrollThreshold) { // Special handling for top area if (scrollTop <= CONFIG.topThreshold) { updateHeaders(true); } // Show on upward scroll else if (scrollDiff < 0) { updateHeaders(true); } // Hide on downward scroll else if (scrollDiff > 0) { updateHeaders(false); } // Update last scroll position lastScrollTop = scrollTop; } } // Initialization function init() { addStyles(); // Set initial state const initialScrollTop = window.pageYOffset || document.documentElement.scrollTop; updateHeaders(initialScrollTop <= CONFIG.topThreshold); // Add scroll listener window.addEventListener('scroll', () => { requestAnimationFrame(handleScroll); }, { passive: true }); } // Initialize when page is loaded if (document.readyState === 'complete') { init(); } else { window.addEventListener('load', init); } })();