// ==UserScript== // @name 输入框显示密码(密码明文切换) // @namespace http://tampermonkey.net/ // @version 1.3 // @description 点击密码框前的标签文字切换明/密文;表格布局时点击上一个td;无标签时插入切换按钮 // @author You // @match *://*/* // @grant none // @run-at document-idle // @downloadURL https://update.greasyfork.icu/scripts/573007/%E8%BE%93%E5%85%A5%E6%A1%86%E6%98%BE%E7%A4%BA%E5%AF%86%E7%A0%81%EF%BC%88%E5%AF%86%E7%A0%81%E6%98%8E%E6%96%87%E5%88%87%E6%8D%A2%EF%BC%89.user.js // @updateURL https://update.greasyfork.icu/scripts/573007/%E8%BE%93%E5%85%A5%E6%A1%86%E6%98%BE%E7%A4%BA%E5%AF%86%E7%A0%81%EF%BC%88%E5%AF%86%E7%A0%81%E6%98%8E%E6%96%87%E5%88%87%E6%8D%A2%EF%BC%89.meta.js // ==/UserScript== (function () { 'use strict'; function processPasswordInput(input) { if (input.dataset.pwdToggled) return; input.dataset.pwdToggled = 'true'; let isVisible = false; const toggle = () => { isVisible = !isVisible; input.type = isVisible ? 'text' : 'password'; }; // ── 策略1:标准 label[for] 或父级 label ── let label = null; if (input.id) { label = document.querySelector(`label[for="${input.id}"]`); } if (!label) { label = input.closest('label'); } if (!label) { const prev = input.previousElementSibling; if (prev && /^(label|span|div|p)$/i.test(prev.tagName)) { label = prev; } } if (label) { bindLabel(label, toggle, () => isVisible); return; } // ── 策略2:表格布局 —— 寻找同行的上一个 ── const currentTd = input.closest('td'); if (currentTd) { const prevTd = currentTd.previousElementSibling; if (prevTd && prevTd.tagName === 'TD') { // 优先找 td 内的 label/span,没有则直接用 td 本身 const inner = prevTd.querySelector('label, span, p, div'); const target = inner || prevTd; bindLabel(target, toggle, () => isVisible); return; } } // ── 策略3:兜底 —— 在输入框后插入切换按钮 ── insertToggleButton(input, toggle, () => isVisible); } // 给任意元素绑定点击切换 function bindLabel(el, toggle, getVisible) { el.style.cursor = 'pointer'; el.title = '点击切换密码显示'; el.addEventListener('click', (e) => { e.preventDefault(); toggle(); const visible = getVisible(); el.style.color = visible ? '#1a7fd4' : ''; el.title = visible ? '点击隐藏密码' : '点击显示密码'; }); } // 兜底按钮 function insertToggleButton(input, toggle, getVisible) { const btn = document.createElement('button'); btn.type = 'button'; btn.textContent = '显示'; btn.setAttribute('aria-label', '切换密码显示'); const h = window.getComputedStyle(input).height; btn.style.cssText = ` display: inline-flex; align-items: center; justify-content: center; height: ${h}; padding: 0 10px; margin-left: 6px; font-size: 13px; line-height: 1; cursor: pointer; border: 1px solid #ccc; border-radius: 4px; background: #f5f5f5; color: #333; vertical-align: middle; box-sizing: border-box; flex-shrink: 0; `; btn.addEventListener('mouseenter', () => { btn.style.background = getVisible() ? '#dceeff' : '#e0e0e0'; }); btn.addEventListener('mouseleave', () => { btn.style.background = getVisible() ? '#dceeff' : '#f5f5f5'; }); btn.addEventListener('click', () => { toggle(); const visible = getVisible(); btn.textContent = visible ? '隐藏' : '显示'; btn.style.background = visible ? '#dceeff' : '#f5f5f5'; btn.style.color = visible ? '#1a7fd4' : '#333'; btn.style.borderColor = visible ? '#1a7fd4' : '#ccc'; }); if (input.nextSibling) { input.parentNode.insertBefore(btn, input.nextSibling); } else { input.parentNode.appendChild(btn); } } function scanAllPasswords() { document.querySelectorAll('input[type="password"]').forEach(processPasswordInput); } scanAllPasswords(); const observer = new MutationObserver(scanAllPasswords); observer.observe(document.body, { childList: true, subtree: true }); })();