输入掩码和UX表单
1)原则
1.帮忙,不要惩罚。掩码引导输入并减少错误,但不阻止打印和插入。
2.数据≠显示。我们保留"原始"归一化值,仅在UI中格式化。
3.可预测的光标。任何自动安装都不会"跳跃"caret或打破undo/redo。
4.位置和设备。按区域和平台划分键盘、分隔符、日历和货币。
5.可访问性和隐私性。文本+图标/颜色;敏感字段掩盖但不干扰密码管理器/自动完成。
2)口罩何时合适(何时不合适)
使用:- 具有可持续结构的格式:电话,IBAN,PAN(地图),CVC,日期,时间,索引,OTP。
- 带分隔符的现金金额(打印时为"净"输入,蓝调时为格式)。
- 代码(裁判。编码,促销),固定长度。
- 名称/地址/电子邮件(掩码限制有效字符/语言)。
- 复杂的免费字段(评论,公司名称)。
- 输入格式可能未知(没有国家/地区的国际编号)。
3)面具vs自动格式vs验证
掩码是"即时"结构的提示(括号,连字符);不得断开输入/插入。
自动格式-适用于蓝色/焦点损失(数千个,IBAN空格)。
验证是正确性逻辑(长度,校验和和),在"blur"或"submit"之后显示错误。
规则:掩码不能取代验证,自动格式不应改变输入的含义。
4)键盘和HTML属性
选择正确的类型/模式以加快输入速度并减少错误:5)Caret,copipast和正常化
不要打破caret:在自动插入字符(空格/括号)时,调整光标位置。
Copipast:插入时,清除空格/连字符→验证→显示格式。
正常化:trimming,替换字符的"曲线"("O'→'0"不能!),翻译成大写的IBAN,统一日期格式的存储(ISO)。
js const clean = s => s. replace(/[^\da-zA-Z]/g,'');
const normalizePAN = s => clean(s). slice (0.19) ;//no spaces/hyphens const normalizeIBAN = s => clean (s). toUpperCase(); // A–Z0–9
6)数字,货币和地方
输入"如何打印"(公差","或"作为分隔符"),存储在小单位(便士/美分)中。
在blure/sabmite后沿位置(数千人分组)显示;聚焦显示"原始"值,方便编辑。
明确指定货币并固定精度(例如,2个符号)。
js function parseMoney(input) {
//resolve both comma and period as decimal const s = input. replace(/\s/g,''). replace(',', '.');
const num = Number(s);
if (Number. isNaN(num)) return null;
return Math. round(num 100); // cents
}
function formatMoney(cents, locale='ru-RU', currency='RUB') {
return (cents/100). toLocaleString(locale, { style:'currency', currency });
}
7)日期和时间
如果本地采样器在平台上不舒服/不同-使用带有"DD"面罩的文本框。MM.YYYY',但保留ISO 'YYYY-MM-DD'。
日期现实检查(29.02,频段),时间段在服务器上。
添加今天、现在、清除按钮。
8)电话和国家
两个字段:国家/地区(+代码)和选定国家/地区的数字或"智能"面具。
插入完整的'+CC……'时,自动填充国家/地区。
存储E.164('+CCXXXXXXXX),在本地显示空格。
9)付款详情: PAN/IBAN/CVC/EXP
PAN:4-4-4-4分组;含义仅为数字;Luhn-check;PAN没有记录/分析。
CVC:"password"样式(隐藏),"autocomplete="cc-csc",不保存到草稿中。
EXP:"MM/YY",2位数字后自动插入"/",检查范围01-12和合理的一年。
IBAN:上例,仅在UI中存在空白;国家长度和校验和检查。
10)OTR/确认代码
6(或N)具有自动对焦和自动转换的单元格,从缓冲区插入可识别所有代码。
"autocomplete="one-time-code",在移动上-从SMS中自动生成。
没有拆分字段(一个字段)的备用输入-用于屏幕阅读器。
html
<div class="otp" role="group" aria-label="Код из SMS">
<input inputmode="numeric" maxlength="1">
<input inputmode="numeric" maxlength="1">
<!-- … -->
</div>
11)面具和a11 y
标签是必需的('<label for>'),placeholder是示例而不是替换。
旁边解释规则:带示例的帮助文本("格式:+CC XXX-XX-XX")。
通过"aria-describedby"链接错误,关键是"role="alert"。
文本和轮廓的对比≥ AA,":focus-visible"不隐藏。
12)隐私和安全
敏感字段:不要拼写、写入RUM,不要保存到草稿中(PAN、CVC、护照)。
口罩和格式不必透露帐户的有效性("如果电子邮件已注册……"-中性表述)。
关键军刀的相容性和可追溯性(付款/费率)。
13)表格行为和性能
异步检查(250-400毫秒),可见指示"我们检查……"。
不要为了一个字段而阻挡整个屏幕;本地旋转器/骨架。
Batchit DOM更改;仅为"转换/操作性"动画。
在移动上-当键盘出现时避免"跳跃"(安全区域,视觉运动元)。
14)代码嗅觉
柔软的手机面罩(无插入片段):js function formatPhoneVisible(value) {
const d = value. replace(/\D/g,''). slice(0,15);
if (!d) return '';
if (d. startsWith('7') d. startsWith('8')) {
return d. replace(/^([78])? (\d{3})(\d{3})(\d{2})(\d{2})./, '+7 ($2) $3-$4-$5');
}
// generic E.164 grouping: +CC XXX XXX XX XX return d. replace(/^(\d{1,3})(\d{0,3})(\d{0,3})(\d{0,2})(\d{0,2})./, (m,c1,c2,c3,c4,c5)=>
`+${c1}${c2?` ${c2}`:''}${c3?` ${c3}`:''}${c4?` ${c4}`:''}${c5?` ${c5}`:''}`.trim());
}
const input = document. querySelector('#phone');
input. addEventListener('input', e => {
const raw = e. target. value;
const pos = e. target. selectionStart;
const digitsBefore = raw. slice(0,pos). replace(/\D/g,''). length;
const cleaned = raw. replace(/[^\d+]/g,'');
const visible = formatPhoneVisible(cleaned);
e. target. value = visible;
// restore caret by counting digits let p = 0, count = 0;
while (p < e. target. value. length && count < digitsBefore) { if (/\d/.test(e. target. value[p])) count++; p++; }
e. target. setSelectionRange(p, p);
});
总和:"焦点原始→蓝调格式":
js const amount = document. getElementById('amount');
let cents = null;
amount. addEventListener('focus', () => {
if (cents!=null) amount. value = String(cents/100). replace('.', ',');
});
amount. addEventListener('blur', () => {
const v = parseMoney(amount. value) ;//from section 6 if (v = = null) return; cents = v;
amount. value = formatMoney(cents, 'ru-RU', 'RUB');
});
IBAN:上例和蓝调分组:
js const iban = document. getElementById('iban');
iban. addEventListener('input', () => iban. value = iban. value. toUpperCase());
iban. addEventListener('blur', () => {
const raw = normalizeIBAN(iban. value);
iban. dataset. raw = raw ;//for iban submission. value = raw. replace(/(.{4})/g,'$1 '). trim () ;//view only
});
15)设计系统令牌(示例)
json
{
"input": {
"radius": 10,
"height": { "sm": 36, "md": 40, "lg": 48 },
"gap": 8,
"icon": 16
},
"mask": {
"debounceMs": 300,
"otpLength": 6,
"moneyPrecision": 2,
"phoneMaxDigits": 15
},
"a11y": {
"focusRing": { "width": 2, "offset": 2 },
"contrastAA": true
}
}
CSS预设:
css
.input { height:40px; padding:0 12px; border-radius:10px; }
.input:focus-visible { outline:2px solid var(--focus-ring); outline-offset:2px; }
.field-error { color: var(--role-danger); font-size:.875rem; margin-top:6px; }
.otp input { width:40px; text-align:center; }
16) iGaming的细节
付款/结论:PAN/IBAN/带有软口罩的 amount;严格的等速率,并且没有敏感场的标记;关于佣金和时机的线索。
KYC:日期口罩,护照号码(没有"硬"过滤功能-考虑不同格式),文件大小/类型,预览。
限制和负责任的游戏:可理解的金额/时期(天/周/月),旁边的辅助器,AAA对比。
投注:快速输入总和(预设按钮+字段),以"原始"数字为焦点,并按位置排列;","……./双分隔符。
17)反模式
硬遮色片,禁止有效字符/插入。
自动格式的跳跃跑车;分配丢失/undo。
Placeholder代替标签。
在值内自动添加货币(打破共计)。
"每个字符"错误,没有删除。
存储中与本地相关的格式(存储ISO/数字)。
编制PAN/护照号码并显示"太诚实"的拒绝原因。
18)度量与实验
字段上的错误率(掩码之前/之后)。
时间到完整表单和重新发送。
未成功插入(copipast)和"回滚"(undo)的比例。
线索/示例的CTR,自动完成的比例。
付款步骤/KUS上的Abandon率。
19) QA支票清单
输入和caret
- 从缓冲区插入不会断裂,空格/连字符会正确清除。
- Caret在自动格式后仍可预测。
本地和格式
- 金额允许','/';存储在小单位中。
- 日期待定并确认;存储在ISO中。
A11y
- 标签和'aria-describedby'连接;'role='alert'用于关键。
- 对比度和焦环对应于AA。
安全性
- 敏感字段不被编译/缓存。
- 在关键的步骤上具有相容性和回归性。
UX
- Placeholder是一个示例,不是标签;帮助者就在附近。
- 口罩不会阻止在移动上打印;正确的键盘("inputmode")。
20)设计系统中的文档
Компоненты: `MaskedInput`, `MoneyInput`, `PhoneInput`, `OtpInput`, `IbanInput`.
面具令牌(长度/模板),caret/插入规则,数字/日期的本地化。
Gaids的隐私性(不用计算),可用性和自动格式vs blur。
"Do/Do n't"带有实际示例和之前或之后的度量标准。
简短摘要
口罩和表单在加速输入、保持数据清洁和不干扰时效果良好。格式化,在入口处规范化,以稳定的视图存储,考虑位置和可用性。然后,表格变得快速易懂-尤其是在敏感的支付,KYC和投注场景中。