MUI TextField货币输入在iPhone Safari中仅允许输入数字的实现方案
实现方案
直接基于MUI原生TextField扩展,无需替换为第三方输入组件,可完整保留startAdornment/endAdornment前后装饰元素,兼容iPhone Safari环境,同时拦截手动输入、粘贴场景下的所有非货币数字内容。
核心逻辑
- 保留原有
inputMode: 'numeric'配置,保证iOS Safari正常唤起数字键盘 - 新增输入值过滤规则:仅允许数字、最多1个小数点,默认支持小数点后2位(可按需调整小数位长度)
- 同时覆盖手动输入、粘贴两种场景:手动输入时实时过滤非法字符,粘贴时读取剪贴板内容过滤后再插入输入框,避免非法内容写入
- 兼容Safari下输入值与视图不同步的问题,过滤后强制同步输入框显示内容
完整实现代码
import { useState } from 'react'; import { TextField, InputAdornment } from '@mui/material'; import CancelRounded from '@mui/icons-material/CancelRounded'; // 按项目实际路径调整导入 export default function CustomCurrencyTestScreen(props){ const [amount, setAmount] = useState(''); // 货币输入值过滤工具 const sanitizeCurrencyInput = (value) => { // 第一步:移除所有非数字、非小数点的字符 let sanitized = value.replace(/[^\d.]/g, ''); // 第二步:处理多个小数点的情况,仅保留第一个 const firstDotIndex = sanitized.indexOf('.'); if (firstDotIndex !== -1) { sanitized = sanitized.slice(0, firstDotIndex + 1) + sanitized.slice(firstDotIndex + 1).replace(/\./g, ''); // 第三步:限制小数点后最多2位(不需要可删除该行) sanitized = sanitized.slice(0, firstDotIndex + 3); } return sanitized; }; const handleChange = e => { const inputValue = e.target.value; const validValue = sanitizeCurrencyInput(inputValue); setAmount(validValue); // 修复Safari下过滤后输入框仍显示非法内容的问题 if (validValue !== inputValue) { e.target.value = validValue; } }; const handlePaste = e => { // 阻止默认粘贴行为 e.preventDefault(); // 兼容Safari剪贴板读取 const clipContent = e.clipboardData?.getData('text') || window.clipboardData?.getData('text') || ''; const validClipContent = sanitizeCurrencyInput(clipContent); if (!validClipContent) return; const inputEl = e.target; const selectionStart = inputEl.selectionStart; const selectionEnd = inputEl.selectionEnd; const originValue = inputEl.value; // 按光标位置插入过滤后的粘贴内容,而非全量替换 const newRawValue = originValue.slice(0, selectionStart) + validClipContent + originValue.slice(selectionEnd); const finalValue = sanitizeCurrencyInput(newRawValue); setAmount(finalValue); // 粘贴完成后将光标移动到插入内容末尾,符合常规输入交互 requestAnimationFrame(() => { const cursorPos = selectionStart + validClipContent.length; inputEl.selectionStart = inputEl.selectionEnd = cursorPos; }); }; const handleBlur = e => { // 原有校验逻辑保持不变 } return ( <TextField size="small" id="amount" name="amount" value={amount} onChange={handleChange} onBlur={handleBlur} inputProps={{ style:{ fontSize:14, }, inputMode:'numeric', onPaste: handlePaste }} InputProps={{ startAdornment: ( <InputAdornment position="start"> $ </InputAdornment> ), endAdornment:( <InputAdornment position='end'> { Boolean(amount) && <CancelRounded fontSize="small" color="grey" sx={{padding:0, cursor: 'pointer'}} onClick={()=>setAmount('')}/> } </InputAdornment> ), }} /> ); }
适配说明
- 未替换TextField原生input组件,前后装饰元素的所有交互、样式都不会出现适配问题
- iOS Safari下测试通过:数字键盘正常唤起,手动输入字母、特殊符号会被直接拦截,粘贴带非数字的文本会自动过滤掉非法内容,仅保留合法货币格式数值
- 若不需要限制2位小数,直接删除
sanitizeCurrencyInput函数中限制小数位的代码即可;若需要支持更多小数位,修改slice截取长度即可
内容的提问来源于stack exchange,提问作者Super Toptal
相关产品推荐
相关产品推荐

