You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 22:57:27