您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在 Twitter/X 搜索框下方添加日期筛选快捷按钮
// ==UserScript== // @name Twitter/X 搜索日期快捷按钮 // @namespace http://tampermonkey.net/ // @version 2025-06-25 // @description 在 Twitter/X 搜索框下方添加日期筛选快捷按钮 // @author Kai([email protected]) // @license MIT // @match https://x.com/explore // @match https://x.com/search* // @icon https://www.google.com/s2/favicons?sz=64&domain=x.com // @grant none // ==/UserScript== (function() { 'use strict'; // 调试开关 const DEBUG = false; const log = (...args) => DEBUG && console.log(...args); log('[日期快捷按钮] 脚本已加载'); // 获取格式化日期 function getFormattedDate(daysAgo = 0) { const date = new Date(); date.setDate(date.getDate() - daysAgo); return date.toISOString().split('T')[0]; } // 获取上周的起止日期 function getLastWeekDates() { const today = new Date(); const dayOfWeek = today.getDay(); const lastSunday = new Date(today); lastSunday.setDate(today.getDate() - dayOfWeek - 7); const lastSaturday = new Date(lastSunday); lastSaturday.setDate(lastSunday.getDate() + 6); return { since: lastSunday.toISOString().split('T')[0], until: lastSaturday.toISOString().split('T')[0] }; } // 获取上个月的起止日期 function getLastMonthDates() { const today = new Date(); const firstDay = new Date(today.getFullYear(), today.getMonth() - 1, 1); const lastDay = new Date(today.getFullYear(), today.getMonth(), 0); return { since: firstDay.toISOString().split('T')[0], until: lastDay.toISOString().split('T')[0] }; } // 获取去年的起止日期 function getLastYearDates() { const today = new Date(); const firstDay = new Date(today.getFullYear() - 1, 0, 1); const lastDay = new Date(today.getFullYear() - 1, 11, 31); return { since: firstDay.toISOString().split('T')[0], until: lastDay.toISOString().split('T')[0] }; } // 创建快捷按钮容器 function createShortcutButtons() { log('[日期快捷按钮] 创建按钮容器'); const container = document.createElement('div'); container.style.cssText = ` display: flex; flex-wrap: wrap; gap: 8px; padding: 12px 16px; border-bottom: 1px solid rgb(239, 243, 244); background: white; `; container.id = 'date-shortcuts-container'; // 按钮配置 const buttons = [ { text: '今天', days: 0 }, { text: '7天内', days: 7 }, { text: '30天内', days: 30 }, { text: '上周', type: 'lastWeek' }, { text: '上个月', type: 'lastMonth' }, { text: '去年', type: 'lastYear' } ]; buttons.forEach(btn => { const button = document.createElement('button'); button.textContent = btn.text; button.style.cssText = ` padding: 6px 12px; border-radius: 9999px; border: 1px solid rgb(207, 217, 222); background: white; color: rgb(15, 20, 25); font-size: 14px; cursor: pointer; transition: all 0.2s; `; // 悬停效果 button.onmouseover = () => { button.style.background = 'rgb(247, 249, 249)'; }; button.onmouseout = () => { button.style.background = 'white'; }; // 点击事件 button.onclick = (e) => { e.preventDefault(); e.stopPropagation(); const searchInput = document.querySelector('[data-testid="SearchBox_Search_Input"]'); if (!searchInput) { log('[日期快捷按钮] 未找到搜索输入框'); return; } const currentValue = searchInput.value.trim(); log(`[日期快捷按钮] 输入框当前值: "${currentValue}"`); // 移除已有的 since: 和 until: 参数 let cleanedValue = currentValue.replace(/\s*since:\S+/g, '').replace(/\s*until:\S+/g, '').trim(); let newQuery; if (btn.days !== undefined) { // 原有的按钮逻辑(只有 since) const dateStr = getFormattedDate(btn.days); newQuery = cleanedValue + ` since:${dateStr}`; } else if (btn.type) { // 新按钮逻辑(有 since 和 until) let dates; switch (btn.type) { case 'lastWeek': dates = getLastWeekDates(); break; case 'lastMonth': dates = getLastMonthDates(); break; case 'lastYear': dates = getLastYearDates(); break; } newQuery = cleanedValue + ` since:${dates.since} until:${dates.until}`; } // 更新 URL 参数 const urlParams = new URLSearchParams(window.location.search); urlParams.set('q', newQuery.trim()); // 构建新 URL const newUrl = `${window.location.pathname}?${urlParams.toString()}`; log(`[日期快捷按钮] 导航到: ${newUrl}`); // 导航到新 URL window.location.href = newUrl; }; container.appendChild(button); }); return container; } // 插入快捷按钮到 dropdown function insertShortcuts() { // 查找 dropdown const dropdown = document.querySelector('[id^="typeaheadDropdown"]'); if (!dropdown) { log('[日期快捷按钮] 未找到 dropdown'); return; } // 检查是否已插入 if (dropdown.querySelector('#date-shortcuts-container')) { log('[日期快捷按钮] 快捷按钮已存在'); return; } log('[日期快捷按钮] 找到 dropdown,准备插入按钮'); // 创建并插入按钮容器到顶部 const shortcuts = createShortcutButtons(); dropdown.prepend(shortcuts); log('[日期快捷按钮] 按钮插入成功'); } // 监听搜索框点击 function setupSearchBoxListener() { const searchInput = document.querySelector('[data-testid="SearchBox_Search_Input"]'); if (!searchInput) { log('[日期快捷按钮] 未找到搜索框,稍后重试'); setTimeout(setupSearchBoxListener, 1000); return; } log('[日期快捷按钮] 找到搜索框,添加监听器'); // 移除旧的监听器(如果存在) searchInput.removeEventListener('click', handleSearchClick); searchInput.removeEventListener('focus', handleSearchClick); searchInput.removeEventListener('keydown', handleSearchSubmit); // 添加新的监听器 searchInput.addEventListener('click', handleSearchClick); searchInput.addEventListener('focus', handleSearchClick); // 监听回车键,确保 URL 参数与输入框内容同步 searchInput.addEventListener('keydown', handleSearchSubmit); // 监听 DOM 变化,以便在 dropdown 出现时插入按钮 const observer = new MutationObserver((mutations) => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === 1 && node.id && node.id.includes('typeaheadDropdown')) { log('[日期快捷按钮] 检测到 dropdown 创建'); setTimeout(insertShortcuts, 50); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); } // 处理搜索框点击事件 function handleSearchClick() { log('[日期快捷按钮] 搜索框被点击'); // 延迟执行以等待 dropdown 创建 setTimeout(insertShortcuts, 100); } // 处理搜索框回车提交 function handleSearchSubmit(e) { if (e.key === 'Enter') { log('[日期快捷按钮] 检测到回车键'); const searchInput = e.target; const currentValue = searchInput.value.trim(); // 如果输入框为空,导航到 explore 页面 if (!currentValue) { e.preventDefault(); window.location.href = '/explore'; return; } // 阻止默认行为,手动处理导航 e.preventDefault(); // 更新 URL 参数 const urlParams = new URLSearchParams(); urlParams.set('q', currentValue); urlParams.set('src', 'typed_query'); // 构建新 URL const newUrl = `/search?${urlParams.toString()}`; log(`[日期快捷按钮] 手动导航到: ${newUrl}`); // 导航到新 URL window.location.href = newUrl; } } // 页面加载完成后初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', setupSearchBoxListener); } else { setupSearchBoxListener(); } // 处理页面导航(Twitter 是 SPA) let lastUrl = location.href; new MutationObserver(() => { const url = location.href; if (url !== lastUrl) { lastUrl = url; log('[日期快捷按钮] 页面导航,重新初始化'); setTimeout(setupSearchBoxListener, 500); } }).observe(document, { subtree: true, childList: true }); })();