Пишем простым языком о сложных технических процессах

Простое изменение видимости пароля

  • javascript
  • css

Наверняка вы видели на различных веб-сайтах и приложениях поле для ввода пароля и кнопку, которая меняет его видимость. Хотя это простая задача для опытных разработчиков, мы хотели бы написать эту заметку для всех, кто может быть еще не сталкивался с ней или только недавно выбрал профессию веб-разработчика.

Разметка

Для начала давайте разберемся с разметкой. Первое, что нужно понять - чтобы разместить кнопку внутри поля ввода, нужно позиционировать ее относительно какого-либо элемента, но поле ввода само по себе не подходит для этого. Поэтому нам нужен дополнительный элемент обёртка, которому мы присвоим определенный класс, например, my-password. Внутри этого элемента мы разместим само поле ввода.

html
<div class="my-password">
  <input type="password" name="password">
</div>

Далее добавим саму кнопку, которая будет отвечать за переключение видимости.

html
<div class="my-password">
  <input type="password" name="password">
  <button class="my-password-toggle"></button>
</div>

С разметкой мы закончили, теперь приступим к стилизации.

Стилизация

Начнём с обёртки и нам нужно для неё указать всего лишь одно свойство.

css
.my-password {
  position: relative;
}

Свойство position отвечает за определение типа позиционирования элемента и значение relative позволит нам раположить его дочерние элементы относительно него.

Теперь нам нужно определить стили для нашей кнопки, и для начала сбросим предустановленыые браузерами стили.

css
.my-password-toggle {
  border: none;
  background: none;
}

Укажем расположение и размеры. Абсолютное позиционирование относительно обёртки, прижимаем к верху, правой стороне и нижней части, а также добавим внутренние отступы со всех сторон.

css
.my-password-toggle {
  border: none;
  background: none;

  position: absolute; // [!code ++]
  top: 0; // [!code ++]
  right: 0; // [!code ++]
  bottom: 0; // [!code ++]
  padding: 10px; // [!code ++]
}

Воспользуемся свойствами flexbox для того, чтобы содержимое кнопки было выровнено по центру, также свойством которое отвечает за тип курсора при наведении.

css
.my-password-toggle {
  border: none;
  background: none;

  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  padding: 10px;

  display: flex; // [!code ++]
  align-items: center; // [!code ++]
  justify-content: center; // [!code ++]

  cursor: pointer; // [!code ++]
}

Иконку будем устанавливать с помощью псевдоэлемента ::after и тут всё еще проще. Указываем объязательное свойство content с пустым значением, т.к. без него элемент не будет отображаться. Определим размеры и т.к. мы планируем разместить иконки с помощью свойства background-image, то мы заранее укажем общие свойства background-repeat и background-size.

css
.my-password-toggle::after {
  content: '';

  width: 20px;
  height: 20px;

  background-repeat: no-repeat;
  background-size: contain;
}

И тут самый интересный момент, где сфокусирована вся логика нашей реализации.

css
.my-password input[type=password] + .my-password-toggle::after {
  background-image: url('eye.svg');
}

.my-password input[type=text] + .my-password-toggle::after {
  background-image: url('eye-off.svg');
}

Если вы вдруг не поняли, хотя это очень простой селектор, который позволяет браузеру самому изменять иконку в зависимости от типа поля, и нам останется с помощью JavaScript изменять тип поля.

Скрипт

Последний шаг - написать скрипт который реализует переключение типа поля. Определим переменную в которую поместим все наши элементы обёртки.

js
const passwords = document.querySelectorAll('.my-password');

Пройдемся по ним и определим переменную, в которой будем хранить поле, и сразу же проверим, существует ли оно вообще. Если нет, то прерываем работу функции.

js
const passwords = document.querySelectorAll('.my-password');

passwords.forEach((el) => {
  const input = el.querySelector('input');

  if (!(input instanceof HTMLInputElement)) {
    return;
  }
});

Теперь нужно получить кнопку и также поместить её в переменную.

js
const passwords = document.querySelectorAll('.my-password');

passwords.forEach((el) => {
  const input = el.querySelector('input');

  if (!(input instanceof HTMLInputElement)) {
    return;
  }

  const toggleBtn = el.querySelector('.my-password-toggle');
});

Наконец, нам нужно повесить на событие нажатия кнопки функцию, которая будет переключать тип поля.

js
const passwords = document.querySelectorAll('.my-password');

passwords.forEach((el) => {
  const input = el.querySelector('input');

  if (!(input instanceof HTMLInputElement)) {
    return;
  }

  const toggleBtn = el.querySelector('.my-password-toggle');

  toggleBtn?.addEventListener('click', (e) => {
    e.preventDefault();
    input.type = input.type === 'text' ? 'password' : 'text';
  });
});

Не бойтесь такой записи ?. это всего-лишь оператор опциональной последовательности, которая избавляет от лишней проверки. А в остальном все просто, отменяем действие по умолчанию с помощью preventDefault() и меняем тип поля, если тип text, то на password, иначе text.

Заключение

Спасибо всем за внимание!

html
<div class="my-password">
  <input type="password" name="password">
  <button class="my-password-toggle"></button>
</div>
css
.my-password {
  position: relative;
}

.my-password-toggle {
  background: none;
  border: none;

  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  padding: 10px;

  display: flex;
  align-items: center;
  justify-content: center;

  cursor: pointer;
}

.my-password-toggle::after {
  content: '';
  width: 20px;
  height: 20px;
  background-repeat: no-repeat;
  background-size: contain;
}

.my-password input[type=password] + .my-password-toggle::after {
  background-image: url('eye.svg');
}

.my-password input[type=text] + .my-password-toggle::after {
  background-image: url('eye-off.svg');
}
js
const passwords = document.querySelectorAll('.my-password');

passwords.forEach((el) => {
  const input = el.querySelector('input');

  if (!(input instanceof HTMLInputElement)) {
    return;
  }

  const toggleBtn = el.querySelector('.my-password-toggle');

  toggleBtn?.addEventListener('click', (e) => {
    e.preventDefault();
    input.type = input.type === 'text' ? 'password' : 'text';
  });
});