KB_Shayan
KB_Shayan

Reputation: 652

How to change password visibility when clicking outside of a password field

I am trying to create a password field that allows the user to click the eye icon to view or hide their password. However, on top of this, I would like to hide the password automatically when the user clicks outside the field. this requires a click and blur event. The problem is the blur event runs before the click event and runs when I click the eye icon. This causes the field to be toggled twice because it runs in the blur event block, then in the click event block.

I basically need it to allow me to toggle by clicking the eye icon and also set the field back to password type when exiting the field. I tried using onmousedown but it behaves really weird, it only works when I am stepping through on 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>

Upvotes: 2

Views: 2276

Answers (4)

Jon P
Jon P

Reputation: 19772

While I think I prefer isherwoods answer I'll leave this here for something else to think about

Here we use css to toggle the icon based on the type attribute of the preceding input element. A little use of the element inspector revealed the unicode for the content of the icon.

Next we use a div to combine the elements addingtabindex=0 again to make it "blurable". When this combined element loses focus, then set the input back to 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>

Upvotes: 0

Amirreza Noori
Amirreza Noori

Reputation: 1525

By monitoring time, repeated action could be detected. In the following code, the toggleWhenExiting function also calls the togglePassword function for toggling the view. The main key is preventing togglePassword function from running twice in less than 200ms. var timems = (new Date()).getTime() is used to get the time in ms and if(timems - $(element).attr('lastrun') < 200) return; calculate time distance from last run.

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>

Upvotes: 2

isherwood
isherwood

Reputation: 61083

We can simplify and clarify things by using constants for the two elements and using jQuery's one() method to create an event handler that only fires once. Notice that I've removed all event handlers from the markup to modernize with separation of concerns.

We can also check the relatedTarget on blur and only take action if it's not the toggle button. Note that I've added tabindex="0" to that element to make it focusable. Without that it isn't reported as the blur target.

$('#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>

Upvotes: 6

Antti_M
Antti_M

Reputation: 968

You could just remove any function from the eye icon altogether if it is in opened state. Let your blur callback take care of all of it!

Upvotes: -2

Related Questions