Реализация ввода пароля
столкнулся с проблемой , хотел сделать 6 раздельных инпутов, в которые можно будет вводить цифры, чтобы при вводе цифры, меня сразу перебрасывало на следующий инпут, я сделал, но работает оно очень косячно, я ввел одну цифру , чтобы перейти на другой блок мне надо нажать на него, хотел бы сделать так, чтобы при вводе цифры в блок, меня сразу перебрасывало на следующий ?
.inputs {
display: flex;
width: 50px;
background: rgba(248, 248, 248, 0.2);
height: 20px;
border-radius: 20px;
align-items: center;
}
.main__inputs {
display: flex;
justify-content: space-between;
align-items: center;
}
<div class="main__inputs">
<input class="inputs">
<input class="inputs">
<input class="inputs">
<input class="inputs">
<input class="inputs">
<input class="inputs">
</div>
Ответы (1 шт):
Автор решения: Neverm1ndo
→ Ссылка
Вариант в виде отдельного компонента с теневым деревом. Не идеально, код еще можно дополнить/улучшить.
Использовал свойства элементов Element.previousElementSibling и Element.nextElementSibling, чтобы менять фокус на следующий/предыдущий инпут.
class HTMLSplittedInput extends HTMLElement {
#hostAttributes = ['items', 'type'];
#items = 6;
#type = 'text';
#shadow;
set items(value) {
if (isNaN(+value)) return;
this.#items = +value;
}
set type(value) {
this.#type = value;
}
constructor() {
super();
this.#shadow = this.attachShadow({ mode: 'closed'}); // присоединяем закрытое теневое дерево, к элементам нет доступа из document
this.#initAttributes();
this.#fill();
this.#setStyles();
this.#handleInputs();
}
/**
* Добавляем стили в теневое дерево для инпутов
*/
#setStyles() {
let style = document.createElement('style');
style.textContent = `input {
display: flex;
width: 50px;
background: rgba(248, 248, 248, 0.2);
height: 20px;
border-radius: 20px;
text-align: center;
}`;
this.#shadow.prepend(style);
}
/**
* Заполняем элемент инпутами, кол-во зависит от аттрибута items
*/
#fill() {
for (let i = 0; i < this.#items; i++) {
const input = document.createElement('input');
input.type = this.#type;
this.#shadow.append(input);
}
}
/**
* собираем значения с аттрибутов
*/
#initAttributes() {
for(const attr of this.attributes) {
if (this.#hostAttributes.includes(attr.name)) {
this[attr.name] = attr.value;
continue;
}
}
}
/**
* Обрабатываем нажатия с клавиатуры
*/
#handleInputs() {
this.addEventListener('keydown', (event) => {
event.preventDefault(); // иначе получим двойной ввод
const current = this.#shadow.activeElement; // текущий элемент в фокусе
if (current instanceof HTMLInputElement == false) return; // проверка на то, что в фокусе инпут
if (event.key == 'Backspace') { // если нажат backspace удаляем значение из поля
current.value = '';
if (current.previousElementSibling) current.previousElementSibling.focus(); // идем к предыдущему
return;
}
if (isNaN(+event.key)) { // проверка на число
current.value = current.value;
return;
}
if (!current.nextElementSibling) { // если больше инпутов не осталось
current.value = event.key;
return void this.#end();
}
current.value = event.key; // записываем в поле значение
current.nextElementSibling.focus(); // идем к следующему
});
}
/**
* Проверяет все значения инпутов, записывает в массив и отправляет в виде эвента end
*/
#end() {
let current = this.#shadow.firstElementChild.nextElementSibling; // первым идет <style>
const values = [current.value];
while (values.length < this.#shadow.children.length - 1) {
current = current.nextElementSibling;
values.push(current.value);
}
this.dispatchEvent(new CustomEvent('end', { detail: values }));
}
}
// определяем пользовательский элемент split-input
customElements.define('splitted-input', HTMLSplittedInput);
const splitted = document.querySelector('splitted-input');
splitted.addEventListener('end', (event) => { // добавляем слушатель эвента end
console.log(event.detail);
});
splitted-input {
display: flex;
justify-content: space-between;
align-items: center;
}
<splitted-input items="6" type="text"></splitted-input>