NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Password Visibility Toggle // @namespace http://tampermonkey.net/ // @version 1.1 // @description Adds a show/hide toggle to password fields on specified websites without modifying server-side code. // @author Your Name // @match https://your-target-website.com/* // @match http://your-target-website.com/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; /** * Configuration Variables */ const config = { // SVG Icons for Show and Hide (You can replace these with your preferred icons) showIcon: ` <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" width="20" height="20" fill="#555"> <path d="M572.52 241.4C518.3 135.6 407.3 64 288 64S57.7 135.6 3.48 241.4a48.16 48.16 0 0 0 0 29.2C57.7 376.4 168.7 448 288 448s230.3-71.6 284.52-177.4a48.16 48.16 0 0 0 0-29.2zM288 400c-79.4 0-152.1-35.1-200-88 47.9-52.9 119.6-88 200-88s152.1 35.1 200 88c-47.9 52.9-119.6 88-200 88zm0-176a88 88 0 1 0 88 88 88.1 88.1 0 0 0-88-88zm0 144a56 56 0 1 1 56-56 56 56 0 0 1-56 56z"/> </svg> `, hideIcon: ` <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" width="20" height="20" fill="#555"> <path d="M320 400c-79.4 0-152.1-35.1-200-88 22.1-24.5 47.9-47.6 75.4-68.2l-46.5-46.5c-6.3 2.7-12.7 6.1-19 10.1C89.5 194.1 32 224 32 224s55.6 112.3 160 192c19.1 12.7 39.1 23.1 59.6 31.8l-46.5-46.5c20.6-27.5 43.7-53.3 68.2-75.4C384.9 272.1 320 400 320 400zM633.8 458.1l-86.7-86.7C588.1 329.7 608 274.4 608 224c0-48.6-35.3-89.6-80-104.6l-52.8-52.8C476.1 69.6 502.5 64 528 64c106 0 160 112.3 160 112.3s-55.6 112.3-160 192c-17.6 11.2-35.7 21.4-54.5 30.2l-52.8-52.8c-14.9-44.7-55.9-80-104.6-80-50.4 0-105.7 19.9-138.4 54.6l-86.7-86.7c-14.2-14.2-37.2-14.2-51.4 0l-16.1 16.1c-14.2 14.2-14.2 37.2 0 51.4l86.7 86.7C70.3 274.3 64 299.4 64 325.8c0 5.6.9 11.2 2.8 16.5l-46.5 46.5C19.2 388.4 0 423.2 0 464c0 48.6 35.3 89.6 80 104.6l52.8 52.8C163.9 560.4 190.3 565 216 565c106 0 160-112.3 160-112.3s-55.6-112.3-160-192c-19.1-12.7-39.1-23.1-59.6-31.8l46.5-46.5c6.3 2.7 12.7 6.1 19 10.1C550.5 317.9 608 288 608 288s-55.6-112.3-160-192c-19.1-12.7-39.1-23.1-59.6-31.8l46.5-46.5c20.6 27.5 43.7 53.3 68.2 75.4C479.1 239.9 544 112 544 112s-64-128-160-128c-25.5 0-51 5.9-73.3 16.2l-46.5-46.5c-19.9 19.9-35.2 43-46.6 68.4l-86.7-86.7c-14.2-14.2-37.2-14.2-51.4 0l-16.1 16.1c-14.2 14.2-14.2 37.2 0 51.4l86.7 86.7C70.3 274.3 64 299.4 64 325.8c0 5.6.9 11.2 2.8 16.5l-46.5 46.5C19.2 388.4 0 423.2 0 464c0 48.6 35.3 89.6 80 104.6l52.8 52.8C163.9 560.4 190.3 565 216 565c106 0 160-112.3 160-112.3s-55.6-112.3-160-192c-19.1-12.7-39.1-23.1-59.6-31.8l46.5-46.5c6.3 2.7 12.7 6.1 19 10.1C550.5 317.9 608 288 608 288s-55.6-112.3-160-192c-19.1-12.7-39.1-23.1-59.6-31.8l46.5-46.5c20.6 27.5 43.7 53.3 68.2 75.4C479.1 239.9 544 112 544 112s-64-128-160-128c-25.5 0-51 5.9-73.3 16.2l-46.5-46.5c-19.9 19.9-35.2 43-46.6 68.4z"/> </svg> `, // Toggle Button Styles toggleButtonStyles: { position: 'absolute', right: '10px', top: '50%', transform: 'translateY(-50%)', background: 'none', border: 'none', cursor: 'pointer', padding: '0', margin: '0', zIndex: '2', display: 'flex', alignItems: 'center', justifyContent: 'center', }, // Wrapper Styles wrapperStyles: { position: 'relative', display: 'inline-block', width: '100%', }, // Icon Styles (if any additional styles are needed) iconStyles: { width: '20px', height: '20px', fill: '#555', }, // Toggle Button Hover Color toggleHoverColor: '#000', // Match URLs (Specify the exact websites where you want to inject the script) // Example: Only run on example.com // @match rules are already defined in the metadata block above }; /** * Utility function to create elements with specific attributes and styles. */ function createElement(tag, attributes = {}, styles = {}) { const element = document.createElement(tag); for (let key in attributes) { if (key === 'innerHTML') { element.innerHTML = attributes[key]; } else { element.setAttribute(key, attributes[key]); } } for (let style in styles) { element.style[style] = styles[style]; } return element; } /** * Inject CSS styles for the toggle button and wrapper. */ function injectStyles() { const style = document.createElement('style'); style.innerHTML = ` .password-toggle-button:hover .password-toggle-icon { fill: ${config.toggleHoverColor}; } `; document.head.appendChild(style); } /** * Function to add toggle button to a password input field. */ function addToggleButton(passwordInput) { // Prevent adding multiple toggle buttons to the same input if (passwordInput.parentElement.classList.contains('password-field-wrapper')) { return; } // Wrap the input in a relative container const wrapper = createElement('div', {}, config.wrapperStyles); passwordInput.parentNode.insertBefore(wrapper, passwordInput); wrapper.appendChild(passwordInput); // Create the toggle button const toggleButton = createElement('button', { type: 'button', class: 'password-toggle-button', 'aria-label': 'Show password', innerHTML: config.showIcon, }, config.toggleButtonStyles); // Append the toggle button to the wrapper wrapper.appendChild(toggleButton); // Event listener for the toggle button toggleButton.addEventListener('click', function() { const type = passwordInput.getAttribute('type') === 'password' ? 'text' : 'password'; passwordInput.setAttribute('type', type); // Update the aria-label and icon if (type === 'password') { toggleButton.setAttribute('aria-label', 'Show password'); toggleButton.innerHTML = config.showIcon; } else { toggleButton.setAttribute('aria-label', 'Hide password'); toggleButton.innerHTML = config.hideIcon; } }); } /** * Function to initialize the script. */ function initialize() { injectStyles(); // Select all password input fields const passwordInputs = document.querySelectorAll('input[type="password"]'); passwordInputs.forEach(input => { addToggleButton(input); }); // Observe for dynamically added password fields const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === 1) { // Element node if (node.matches('input[type="password"]')) { addToggleButton(node); } // Also check within the node for password inputs const nestedPasswordInputs = node.querySelectorAll('input[type="password"]'); nestedPasswordInputs.forEach(nestedInput => { addToggleButton(nestedInput); }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); } // Initialize the script when the DOM is fully loaded if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initialize); } else { initialize(); } })();