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
相关产品推荐
相关产品推荐

