使用反应以使用 onKeyDown 在文本末尾使用 caret/text-cursor 聚焦输入元素

Use react to focus input element with caret/text-cursor at end of text using onKeyDown

我的组件中有两个输入字段:

const MyComponent = () => {

  const onKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
    if (event.key === 'ArrowLeft') {
      ref.current?.setSelectionRange(value.length, value.length)
      ref.current?.focus()
    }
  }

  const [value, setValue] = useState('1234')
  const ref = useRef<HTMLInputElement>(null)

  return (
    <>
      <input
        onChange={({target}) => setValue(target.value)}
        value={value}
        ref={ref}
        type={'text'}/>

      <input
        onKeyDown={onKeyDown}
        type={'text'}/>
    </>
  )
}

当我在第二个输入上按下左箭头键时,第一个输入应该聚焦,光标应该在输入文本的末尾。

但是光标在错误的地方。为什么这不起作用?

光标是否向最后一个字符左侧移动了一个位置?

有趣的是,当使用 onKeyUp(释放密钥)而不是 onKeyDown 时,问题似乎消失了。

https://codesandbox.io/s/cursor-end-of-input-onkeyup-x6ekke?file=/src/App.js

import { useRef, useState } from "react";

const MyComponent = () => {
  const [value, setValue] = useState("1234");
  const ref = useRef(null);

  const onKeyUp = (event) => {
    if (event.key === "ArrowLeft") {
      const textInput = ref.current;
      const len = value.length;

      textInput.setSelectionRange(len, len);
      textInput.focus();
    }
  };

  return (
    <>
      <input
        onChange={({ target }) => setValue(target.value)}
        value={value}
        ref={ref}
        type={"text"}
      />

      <input onKeyUp={onKeyUp} type={"text"} />
    </>
  );
};

export default MyComponent;

我的猜测是,因为 onKeyUp 自然地跟在 onKeyDown 之后,当我们在 React 中仅接收到 onKeyDown 事件时,根据您的示例代码,会发生以下序列(或者一般来说像这样的事情正在发生):

  1. 我们的光标移动到文本输入的最后。 (在 onKeyDown 处理程序中处理)。
  2. 文本输入获得焦点(也在 onKeyDown 处理程序中处理)。
  3. 左箭头键的释放导致 onkeyup 事件到 DOM 中的 运行(我们在 React 中没有做任何处理)而 由于我们之前 运行 的 onKeyDown 处理程序,现在焦点在文本输入 上。在输入焦点上按下并释放左箭头键的默认行为是将光标向左移动一位,将其放置在距输入文本末尾一位的位置。

我找不到好的资源或以前的答案来清楚地解释这一点,但如果您想了解更多信息,希望这有助于初步调查。

坚持你对 onKeyDown 的使用,我不得不在你的事件处理程序中加入 event.preventDefault(),然后事情似乎按预期工作。 setTimeout() 似乎是另一种解决方法。我的猜测是,这些解决方法有效地阻止浏览器完全触发按键按下和向上事件,或者将我们的处理程序从 运行ning 延迟到 after 本机事件分别被触发。

https://codesandbox.io/s/cursor-end-of-input-example-h1yrdr?file=/src/App.js

import { useRef, useState } from "react";

const MyComponent = () => {
  const [value, setValue] = useState("1234");
  const ref = useRef(null);

  const onKeyDown = (event) => {
    if (event.key === "ArrowLeft") {
      /*
       * 1. setTimout() option...
       */
      setTimeout(() => {
        const textInput = ref.current;
        const len = value.length;

        textInput.setSelectionRange(len, len);
        textInput.focus();
      }, 0);

      /*
       * 2. Alternatively, using event.preventDefault()...
       */

      // event.preventDefault();

      //   const textInput = ref.current;
      //   const len = value.length;

      //   textInput.setSelectionRange(len, len);
      //   textInput.focus();
      // }
    }
  };

  return (
    <>
      <input
        onChange={({ target }) => setValue(target.value)}
        value={value}
        ref={ref}
        type={"text"}
      />

      <input onKeyDown={onKeyDown} type={"text"} />
    </>
  );
};

export default MyComponent;