// ==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 });
})(); |