abh*_*bhi 9 javascript cursor-position reactjs controlled-component
我有一个受控的 React 输入组件,我正在格式化输入,如 onChange 代码所示。
<input type="TEL" id="applicantCellPhone" onChange={this.formatPhone} name="applicant.cellPhone" value={this.state["applicant.cellPhone"]}/>
Run Code Online (Sandbox Code Playgroud)
然后我的 formatPhone 功能是这样的
formatPhone(changeEvent) {
let val = changeEvent.target.value;
let r = /(\D+)/g,
first3 = "",
next3 = "",
last4 = "";
val = val.replace(r, "");
if (val.length > 0) {
first3 = val.substr(0, 3);
next3 = val.substr(3, 3);
last4 = val.substr(6, 4);
if (val.length > 6) {
this.setState({ [changeEvent.target.name]: first3 + "-" + next3 + "-" + last4 });
} else if (val.length > 3) {
this.setState({ [changeEvent.target.name]: first3 + "-" + next3 });
} else if (val.length < 4) {
this.setState({ [changeEvent.target.name]: first3 });
}
} else this.setState({ [changeEvent.target.name]: val });
Run Code Online (Sandbox Code Playgroud)
}
当我尝试在中间的某处删除/添加一个数字然后光标立即移动到字符串的末尾时,我开始面临这个问题。
我在Sophie的解决方案中看到了一个解决方案,但我认为这不适用于这里,因为 setState 无论如何都会导致渲染。我还尝试通过 setSelectionRange(start, end) 来操纵插入符号的位置,但这也无济于事。我认为导致渲染的 setState 使组件将编辑后的值视为最终值并导致光标移动到末尾。
谁能帮我弄清楚如何解决这个问题?
恐怕如果你放弃对 React 的控制,状态的改变将不可避免地丢弃插入符位置,因此唯一的解决方案是你自己处理它。
最重要的是,考虑到字符串操作,保留“当前位置”并不是那么简单......
为了尝试更好地解决问题,我使用反应钩子提出了一个解决方案,您可以在其中更好地看到发生了哪些状态变化
function App() {
const [state, setState] = React.useState({});
const inputRef = React.useRef(null);
const [selectionStart, setSelectionStart] = React.useState(0);
function formatPhone(changeEvent) {
let r = /(\D+)/g, first3 = "", next3 = "", last4 = "";
let old = changeEvent.target.value;
let val = changeEvent.target.value.replace(r, "");
if (val.length > 0) {
first3 = val.substr(0, 3);
next3 = val.substr(3, 3);
last4 = val.substr(6, 4);
if (val.length > 6) {
val = first3 + "-" + next3 + "-" + last4;
} else if (val.length > 3) {
val = first3 + "-" + next3;
} else if (val.length < 4) {
val = first3;
}
}
setState({ [changeEvent.target.name]: val });
let ss = 0;
while (ss<val.length) {
if (old.charAt(ss)!==val.charAt(ss)) {
if (val.charAt(ss)==='-') {
ss+=2;
}
break;
}
ss+=1;
}
setSelectionStart(ss);
}
React.useEffect(function () {
const cp = selectionStart;
inputRef.current.setSelectionRange(cp, cp);
});
return (
<form autocomplete="off">
<label for="cellPhone">Cell Phone: </label>
<input id="cellPhone" ref={inputRef} onChange={formatPhone} name="cellPhone" value={state.cellPhone}/>
</form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Run Code Online (Sandbox Code Playgroud)
我希望它有帮助
onChange独自一人是不够的。
情况一:如果target.value === 123|456这时你不知道怎么'-'被删除了。与<del>或 与<backspace>. 所以您不知道结果值和插入符位置应该是12|4-56or 123-|56。
但是如果您要保存以前的插入符号位置和值怎么办?假设之前onChange你有过
123-|456
现在你有
123|456
这显然意味着用户按下了<backspace>。但来了...
情况2:用户可以用鼠标改变光标位置。
onKeyDown为了救援:
function App() {
const [value, setValue] = React.useState("")
// to distinguish <del> from <backspace>
const [key, setKey] = React.useState(undefined)
function formatPhone(event) {
const element = event.target
let caret = element.selectionStart
let value = element.value.split("")
// sorry for magical numbers
// update value and caret around delimiters
if( (caret === 4 || caret === 8) && key !== "Delete" && key !== "Backspace" ) {
caret++
} else if( (caret === 3 || caret === 7) && key === "Backspace" ) {
value.splice(caret-1,1)
caret--
} else if( (caret === 3 || caret === 7) && key === "Delete" ) {
value.splice(caret,1);
}
// update caret for non-digits
if( key.length === 1 && /[^0-9]/.test(key) ) caret--
value = value.join("")
// remove everithing except digits
.replace(/[^0-9]+/g, "")
// limit input to 10 digits
.replace(/(.{10}).*$/,"$1")
// insert "-" between groups of digits
.replace(/^(.?.?.?)(.?.?.?)(.?.?.?.?)$/, "$1-$2-$3")
// remove exescive "-" at the end
.replace(/-*$/,"")
setValue(value);
// "setTimeout" to update caret after setValue
window.requestAnimationFrame(() => {
element.setSelectionRange(caret,caret)
})
}
return (
<form autocomplete="off">
<label for="Phone">Phone: </label>
<input id="Phone" onChange={formatPhone} onKeyDown={event => setKey(event.key)} name="Phone" value={value}/>
</form>
)
}
Run Code Online (Sandbox Code Playgroud)
您可能还对某些用于该任务的库感兴趣。例如https://github.com/nosir/cleave.js但它移动插入符号的方式可能不符合您的口味。不管怎样,它可能不是唯一的图书馆。
| 归档时间: |
|
| 查看次数: |
5365 次 |
| 最近记录: |