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

React中如何让输入框光标始终停留在%符号之前?

React中如何让输入框光标始终停留在%符号之前?

嘿,我完全懂你的困扰——每次输入百分比的时候,光标总自动跳到%符号后面,打字的时候得手动挪回去,太麻烦了对吧?咱们来一步步搞定这个问题~

问题根源

你现在的写法是把%直接拼在了输入框的显示值里,React每次重新渲染输入框时,默认会把光标放在整个字符串的末尾,也就是%的后面。所以咱们的核心思路就是:手动控制光标位置,让它始终停留在数字和%之间。

解决方案步骤

1. 给输入框添加Ref

首先需要获取输入框的DOM元素,这样才能直接操作光标位置,用React的useRef钩子就行:

const feeInputRef = useRef(null);

然后把这个ref绑定到输入框上:

<input
  type='text'
  inputMode='decimal'
  name='lendFee'
  id='lendFee'
  placeholder='0%'
  ref={feeInputRef} {/* 加上这行 */}
  value={lendData.lendFee ? formatPercent(lendData.lendFee) : ''}
  onChange={handleChange}
/>

2. 修改输入处理逻辑,添加光标位置控制

接下来要修改handleChange里处理lendFee的部分,还要在状态更新完成后调整光标位置。注意React的状态更新是异步的,所以要在setLendData的回调里操作光标,确保DOM已经更新:

function handleChange(e) {
  let { name, value } = e.target;
  if (name === 'lendValue') {
    // 你原来的lendValue逻辑保持不变
    value = value.replace(/^0+|[^0-9]/g, '');
    setLendData(prev => ({ ...prev, [name]: value }));
  } else if (name === 'lendFee') {
    // 先处理输入值:去掉开头多余的0,只保留数字、小数点/逗号
    value = value.replace(/^0+(?=\d)|[^\d.,]/g, '');
    // 确保输入里只有一个小数点/逗号(避免多个分隔符的情况)
    const separatorIndex = value.indexOf('.') !== -1 ? value.indexOf('.') : value.indexOf(',');
    if (separatorIndex !== -1) {
      value = value.slice(0, separatorIndex + 1) + value.slice(separatorIndex + 1).replace(/[.,]/g, '');
    }
    // 更新状态,同时在回调里调整光标位置
    setLendData(prev => ({ ...prev, [name]: value }), () => {
      if (feeInputRef.current && lendData.lendFee) {
        const inputElement = feeInputRef.current;
        // 光标位置设为显示值长度减1(因为最后一位是%)
        const cursorPos = inputElement.value.length - 1;
        inputElement.setSelectionRange(cursorPos, cursorPos);
      }
    });
  }
}

3. 优化格式化函数(可选)

可以稍微调整下formatPercent函数,让空值处理更清晰:

function formatPercent(amount) {
  return amount ? `${amount}%` : '';
}

完整代码示例

把所有逻辑整合起来,完整的组件代码大概是这样:

import React, { useState, useRef } from 'react';

function LendForm() {
  const [lendData, setLendData] = useState({
    lendValue: '',
    lendFee: '',
  });
  const feeInputRef = useRef(null);

  function formatPercent(amount) {
    return amount ? `${amount}%` : '';
  }

  function handleChange(e) {
    let { name, value } = e.target;
    if (name === 'lendValue') {
      value = value.replace(/^0+|[^0-9]/g, '');
      setLendData(prev => ({ ...prev, [name]: value }));
    } else if (name === 'lendFee') {
      // 去除开头多余的0,只保留合法字符
      value = value.replace(/^0+(?=\d)|[^\d.,]/g, '');
      // 确保只有一个分隔符(小数点或逗号)
      const dotIndex = value.indexOf('.');
      const commaIndex = value.indexOf(',');
      if (dotIndex !== -1 && commaIndex !== -1) {
        // 同时有两种分隔符,保留先出现的那个
        const firstSepIndex = Math.min(dotIndex, commaIndex);
        value = value.slice(0, firstSepIndex + 1) + value.slice(firstSepIndex + 1).replace(/[.,]/g, '');
      } else if (dotIndex !== -1) {
        value = value.slice(0, dotIndex + 1) + value.slice(dotIndex + 1).replace(/\./g, '');
      } else if (commaIndex !== -1) {
        value = value.slice(0, commaIndex + 1) + value.slice(commaIndex + 1).replace(/,/g, '');
      }
      // 更新状态并调整光标
      setLendData(prev => ({ ...prev, [name]: value }), () => {
        if (feeInputRef.current && lendData.lendFee) {
          const input = feeInputRef.current;
          input.setSelectionRange(input.value.length - 1, input.value.length - 1);
        }
      });
    }
  }

  return (
    <div>
      <input
        type='text'
        inputMode='decimal'
        name='lendFee'
        id='lendFee'
        placeholder='0%'
        ref={feeInputRef}
        value={formatPercent(lendData.lendFee)}
        onChange={handleChange}
      />
    </div>
  );
}

export default LendForm;

关键说明

  • useRef用来获取输入框的DOM节点,这样才能调用setSelectionRange方法精准控制光标位置
  • 在setLendData的回调里操作光标,是因为React状态更新是异步的,必须等DOM重新渲染完成后再调整光标
  • 优化了输入值的校验逻辑,避免出现多个小数点/逗号,同时保留合法的开头单个0(比如0.5%是允许的,但00.5%会被自动处理成0.5%)

备注:内容来源于stack exchange,提问作者flyingduck92

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 10:38:04