Input OTP
Thành phần đầu vào OTP gồm 6 chữ số trực quan sẽ tự động lấy nét khi bạn nhập một ký tự đơn và quay lại hộp trước đó bằng phím lùi. Cũng hỗ trợ dán.
Xem trước
Hộp nhập OTP gồm 6 chữ số. Nhập số sẽ tự động chuyển sang ô tiếp theo.
Trường hợp sử dụng và tính năng
-
Luồng xác thực: Lý tưởng cho việc nhập mã xác thực qua SMS/email, màn hình xác thực hai yếu tố (2FA).
-
Hỗ trợ dán: Dán số từ clipboard sẽ tự động phân bổ chúng vào từng ô.
-
Tối ưu hóa di động:
inputmode="numeric"tự động hiển thị bàn phím số trên điện thoại thông minh. -
Xác thực: Loại trừ các đầu vào không phải là số bằng
replace(/[^0-9]/g, '').
Điểm thiết kế và UX
-
Kích thước hộp 52x60px: Kích thước tối ưu cho khả năng hiển thị cao và thao tác chạm dễ dàng.
-
transform: scale(1.05)on focus: Bạn có thể thấy rõ hộp nào hiện đang được nhập. -
.filledclass: Trực quan hóa tiến trình bằng cách thay đổi màu đường viền của ô đã điền.
Cách tùy chỉnh
Đã đổi thành OTP 4 chữ số
{/* Giảm phần tử đầu vào xuống 4 */}
<input class="otp-box" type="text" inputmode="numeric" maxlength="1" />
<input class="otp-box" type="text" inputmode="numeric" maxlength="1" />
<input class="otp-box" type="text" inputmode="numeric" maxlength="1" />
<input class="otp-box" type="text" inputmode="numeric" maxlength="1" />
Thay đổi kích thước hộp
.otp-box { width: 44px; height: 52px; font-size: 1.25rem; }
Mã triển khai
<div class="otp-inputs" id="otp-inputs">
<input class="otp-box" type="text" inputmode="numeric" maxlength="1" />
<input class="otp-box" type="text" inputmode="numeric" maxlength="1" />
<input class="otp-box" type="text" inputmode="numeric" maxlength="1" />
<input class="otp-box" type="text" inputmode="numeric" maxlength="1" />
<input class="otp-box" type="text" inputmode="numeric" maxlength="1" />
<input class="otp-box" type="text" inputmode="numeric" maxlength="1" />
</div>
<style>
.otp-inputs { display: flex; gap: 0.75rem; }
.otp-box {
width: 52px; height: 60px;
background: rgba(255,255,255,0.06);
border: 2px solid rgba(255,255,255,0.12);
border-radius: 12px;
color: white;
font-size: 1.5rem;
font-weight: 700;
text-align: center;
outline: none;
transition: border-color 0.2s, background 0.2s, transform 0.1s;
}
.otp-box:focus { border-color: #6366f1; background: rgba(99,102,241,0.1); transform: scale(1.05); }
.otp-box.filled { border-color: rgba(99,102,241,0.5); }
</style>
<script>
(function() {
var inputs = Array.from(document.querySelectorAll('#otp-inputs .otp-box'));
inputs.forEach(function(input, i) {
input.addEventListener('input', function(e) {
var val = e.target.value.replace(/[^0-9]/g, '');
e.target.value = val.slice(-1);
if (val && i < inputs.length-1) inputs[i+1].focus();
e.target.classList.toggle('filled', !!e.target.value);
});
input.addEventListener('keydown', function(e) {
if (e.key==='Backspace' && !e.target.value && i>0) {
inputs[i-1].focus(); inputs[i-1].value=''; inputs[i-1].classList.remove('filled');
}
});
input.addEventListener('paste', function(e) {
e.preventDefault();
var text = (e.clipboardData || window.clipboardData).getData('text').replace(/[^0-9]/g, '');
text.split('').forEach(function(ch, j) {
if (inputs[i+j]) { inputs[i+j].value = ch; inputs[i+j].classList.add('filled'); }
});
var nextEmpty = inputs.findIndex(function(inp, j) { return j >= i && !inp.value; });
if (nextEmpty !== -1) inputs[nextEmpty].focus();
});
});
})();
</script> Trạng thái tương thích với trình duyệt
| Trình duyệt | Trạng thái tương thích |
|--------|-------|
| Chrome 66+ | ✅ Hoàn toàn tương thích |
| Firefox 63+ | ✅ Hoàn toàn tương thích |
| Safari 12+ | ✅ Hoàn toàn tương thích |
| Cạnh 79+ | ✅ Hoàn toàn tương thích |
Trên thiết bị di động,
inputmode="numeric"tự động hiển thị bàn phím số, cải thiện đáng kể khả năng sử dụng.