Estoy tratando de crear un campo de contraseña que permita al usuario hacer clic en el ícono del ojo para ver u ocultar su contraseña. Sin embargo, además de esto, me gustaría ocultar la contraseña automáticamente cuando el usuario hace clic fuera del campo. esto requiere un evento de clic y desenfoque. El problema es que el evento de desenfoque se ejecuta antes del evento de clic y se ejecuta cuando hago clic en el ícono del ojo. Esto hace que el campo se alterne dos veces porque se ejecuta en el bloque de eventos de desenfoque y luego en el bloque de eventos de clic.
Básicamente, lo necesito para permitirme alternar haciendo clic en el ícono del ojo y también establecer el campo nuevamente en el tipo de contraseña al salir del campo. Intenté usar onmousedown pero se comporta de manera muy extraña, solo funciona cuando estoy avanzando en devtools.
function togglePassword(element) { $(element).toggleClass("fa-eye fa-eye-slash"); $(element).siblings("input").each(function() { if ($(element).hasClass('fa-eye-slash')) { $(this).attr('type', 'text'); $(this).focus(); } else if ($(element).hasClass('fa-eye')) { $(this).attr('type', 'password'); } }); } function toggleWhenExiting(element) { console.log("focus toggle"); $(element).siblings("span").each(function() { if ($(this).hasClass('fa-eye-slash')) { $(element).attr('type', 'password'); $(this).toggleClass("fa-eye fa-eye-slash"); } }); } <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" integrity="sha512-Fo3rlrZj/k7ujTnHg4CGR2D7kSs0v4LLanw2qksYuRlEzO+tcaEPQogQ0KaoGN26/zrn20ImR1DfuLWnOo7aBA==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <input type="password" id="passwordFirst" class="form-control" onblur="toggleWhenExiting(this)"> <span class="fas fa-eye" id="togglePassword" onclick="togglePassword(this)"></span> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>Podemos simplificar y aclarar las cosas usando constantes para los dos elementos y usando el método one() de jQuery para crear un controlador de eventos que solo se active una vez. Tenga en cuenta que eliminé todos los controladores de eventos del marcado para modernizar con separación de preocupaciones.
También podemos verificar el relatedTarget en el desenfoque y solo tomar medidas si no es el botón de alternancia. Tenga en cuenta que he agregado tabindex="0" a ese elemento para que sea enfocable. Sin eso, no se informa como el objetivo de desenfoque.
$('#togglePassword').click(function() { const eyeIcon = $(this); const pwdInput = $('#passwordFirst'); pwdInput.off('blur'); if (eyeIcon.hasClass('fa-eye')) { eyeIcon.removeClass('fa-eye').addClass('fa-eye-slash'); pwdInput.attr('type', 'text').focus(); pwdInput.one('blur', function(e) { if ($(e.relatedTarget).attr('id') !== 'togglePassword') { pwdInput.attr('type', 'password'); eyeIcon.removeClass("fa-eye-slash").addClass('fa-eye'); } }); } else { eyeIcon.removeClass('fa-eye-slash').addClass('fa-eye'); pwdInput.attr('type', 'password'); } }); <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" integrity="sha512-Fo3rlrZj/k7ujTnHg4CGR2D7kSs0v4LLanw2qksYuRlEzO+tcaEPQogQ0KaoGN26/zrn20ImR1DfuLWnOo7aBA==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <input type="password" id="passwordFirst" class="form-control"> <span class="fas fa-eye" id="togglePassword" tabindex="0"></span> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>Al monitorear el tiempo, se podría detectar una acción repetida. En el siguiente código, la función toggleWhenExiting también llama a la función togglePassword para alternar la vista. La clave principal es evitar que la función togglePassword se ejecute dos veces en menos de 200 ms. var timems = (new Date()).getTime() se usa para obtener el tiempo en ms y if(timems - $(element).attr('lastrun') < 200) return; calcular la distancia de tiempo desde la última ejecución.
function togglePassword(element) { console.log("span clicked"); var timems = (new Date()).getTime() if(timems - $(element).attr('lastrun') < 200) return; $(element).attr('lastrun', timems); $(element).toggleClass("fa-eye fa-eye-slash"); $(element).siblings("input").each(function() { if ($(element).hasClass('fa-eye-slash')) { $(this).attr('type', 'text'); $(this).focus(); } else if ($(element).hasClass('fa-eye')) { $(this).attr('type', 'password'); } }); } function toggleWhenExiting(element) { console.log("focus toggle"); $(element).siblings("span").each(function() { if ($(this).hasClass('fa-eye-slash')) $(this).click(); }); } </script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" integrity="sha512-Fo3rlrZj/k7ujTnHg4CGR2D7kSs0v4LLanw2qksYuRlEzO+tcaEPQogQ0KaoGN26/zrn20ImR1DfuLWnOo7aBA==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <input type="password" id="passwordFirst" class="form-control" onblur="toggleWhenExiting(this)"> <span class="fas fa-eye" id="togglePassword" onclick="togglePassword(this)"></span> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>Si bien creo que prefiero la respuesta de isherwood , dejaré esto aquí para pensar en otra cosa.
Aquí usamos css para alternar el ícono según el atributo de type del elemento de input anterior. Un poco de uso del inspector de elementos reveló el Unicode para el contenido del icono.
A continuación, usamos un div para combinar los elementos agregando tabindex=0 nuevamente para que sea "borrable". Cuando este elemento combinado pierda el foco, vuelva a establecer la entrada en password
/*Toggle password field on eye click*/ $(".togglePassword > .fas").click(function() { //Get currnet status var currentType = $(this).prev("input").attr("type"); //toggle the password field $(this).prev("input").attr("type", currentType == "password" ? "text" : "password"); }) /*Set back to password when our combined element loses focus*/ $(".togglePassword").blur(function() { $(this).find("input").attr("type", "password"); }) /*Use CSS to toggle the eye icon based on type attribute of previous element*/ .togglePassword>[type=password]+.fas::before { /*eye icon*/ content: "\f06e"; } .togglePassword>[type=text]+.fas::before { /*slashed eye icon*/ content: "\f070"; } <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" integrity="sha512-Fo3rlrZj/k7ujTnHg4CGR2D7kSs0v4LLanw2qksYuRlEzO+tcaEPQogQ0KaoGN26/zrn20ImR1DfuLWnOo7aBA==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <div class="togglePassword" tabindex="0"> <input type="password" id="passwordFirst" class="form-control"> <span class="fas"></span> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>