使用单一输入获取一次性代码
有一天晚上,我尝试创建一个单次输入的一次性代码,这很有趣:
一次性代码是一个有效值autocomplete
,只需几行 JS,它就可以让您通过短信(sms)填写字段。
但更常见的是,它被称为OTP。
一次性密码 (OTP),也称为一次性 PIN、一次性授权码 (OTAC) 或动态密码,是在计算机系统或其他数字设备上仅对一次登录会话或交易有效的密码。
回到我的例子。
它使用非常简单的标记:
<input
type="text"
autocomplete="one-time-code"
inputmode="numeric"
maxlength="6"
pattern="\d{6}"
>
CSS 稍微复杂一些:
:where([autocomplete=one-time-code]) {
--otp-digits: 6; /* length */
--otc-ls: 2ch;
--otc-gap: 1.25;
/* private consts */
--_otp-bgsz: calc(var(--otc-ls) + 1ch);
all: unset;
background: linear-gradient(90deg,
var(--otc-bg, #EEE) calc(var(--otc-gap) * var(--otc-ls)),
transparent 0
) 0 0 / var(--_otp-bgsz) 100%;
caret-color: var(--otc-cc, #333);
clip-path: inset(0% calc(var(--otc-ls) / 2) 0% 0%);
font-family: ui-monospace, monospace;
font-size: var(--otc-fz, 2.5em);
inline-size: calc(var(--otc-digits) * var(--_otp-bgsz));
letter-spacing: var(--otc-ls);
padding-block: var(--otc-pb, 1ch);
padding-inline-start: calc(((var(--otc-ls) - 1ch) / 2) * var(--otc-gap));
}
这一堆东西模拟了 6 个字段(来自属性--otc-digits
),但实际上它只有一个<input>
。“字段”之间的间距是由于letter-spacing
,而灰色的“框”来自linear-gradient
。
它必须使用等宽字体,因此魔法值有效1ch
— 同样适用于letter-spacing
.1ch
等于零的宽度。
但为什么?
您以前创建过 OTP 组件吗?
我写“组件”,因为它通常<fieldset>
由六个<input>
s 和一堆 JavaScript 组成,用于检测您何时进入或离开字段等。
当您从Web OTP API填写字段时,您需要拆分值,并填写六个字段而不是一个。
只需一次输入就简单多了 :
navigator.credentials.get({
otp: {transport:['sms']}
})
.then(otp => input.value = otp.code);
突出显示当前“字段”
单次输入的 OTP 并不完美。当你从一个“字段”移动到另一个“字段”时,如果插入符号变成一个方块,用户体验会更好:
.selector {
caret-shape: block;
}
不幸的是,目前还没有浏览器支持caret-shape
。
另一种方法是添加另一个 background-gradient
,但不重复模式:
并通过将尺寸属性 — --_otp-bgsz
— 与数字数字相乘来定位它--_otp-digit
,作为自定义属性:
.selector {
background-position:
calc(var(--_otp-digit, 0) * var(--_otp-bgsz)) 0;
}
这并不完美,因为我们需要将数字放在 CSS 自定义属性中,然后使用 JavaScript 进行更新:
input.addEventListener('input', () =>
input.style.setProperty('--_otp-digit',
input.selectionStart)
)
有没有更简单的方法?还有其他建议吗?请在评论区留言!
这是一个 Codepen 演示:
文章来源:https://dev.to/madsstoumann/using-a-single-input-for-one-time-code-352l