How to implement input auto-tab in React (focus on next input element on keyup event)?

Viewed 8528

Using React as library, I have several blocks of input text, each one with maxlength=1, and I would like to implement a function that everytime a character is entered in an input box the focus goes to the next one.

This is the list of input elements I'm talking about:

enter image description here

And this is a minimal representation on CodesSandbox: https://codesandbox.io/s/react-autotab-6kewb.

How can I get the desired behaviour in React?

This is the relevant snippet:

const autoTab = e => {
  const BACKSPACE_KEY = 8;
  const DELETE_KEY = 46;

  if (e.keyCode === BACKSPACE_KEY) {
    // TODO: implement backwards autoTab
  } else if (e.keyCode !== DELETE_KEY) {
    // TODO: implement forwards autoTab
  }
};

const blocks = Array.from({ length: 10 }, (element, index) => (
  <input className="block" key={index} maxLength={1} onKeyUp={autoTab} />
));
3 Answers

I have added tab indexes to your input elements and used them to navigate between items.

const autoTab = e => {
  const BACKSPACE_KEY = 8;
  const DELETE_KEY = 46;
  let tabindex = $(e.target).attr("tabindex") || 0;
  tabindex = Number(tabindex);
  if (e.keyCode === BACKSPACE_KEY) {
    tabindex -= 1;
  } else if (e.keyCode !== DELETE_KEY) {
    tabindex += 1;
  }
  const elem = $("[tabindex=" + tabindex + "]");
  if (elem[0]) {
    elem.focus();
  }
};

const blocks = Array.from({ length: 10 }, (element, index) => (
  <input
    className="block"
    tabIndex={index}
    key={index}
    maxLength={1}
    onKeyUp={autoTab}
  />
));

EDIT: Here is a new way using refs and the Demo based on your code sandbox.

const autoTab = e => {
  const BACKSPACE_KEY = 8;
  const DELETE_KEY = 46;
  let tabindex = $(e.target).attr("data-index") || 0;
  tabindex = Number(tabindex);
  let elem = null;
  if (e.keyCode === BACKSPACE_KEY) {
    elem = tabindex > 0 && elemRefs[tabindex - 1];
  } else if (e.keyCode !== DELETE_KEY) {
    elem = tabindex < elemRefs.length - 1 && elemRefs[tabindex + 1];
  }
  if (elem) {
    elem.current.focus();
  }
};

const Input = props => {
  const ref = React.createRef();
  elemRefs.push(ref);
  return (
    <input
      className="block"
      data-index={props.index}
      ref={ref}
      maxLength={1}
      onKeyUp={props.autoTab}
    />
  );
};

const blocks = Array.from({ length: 10 }, (element, index) => (
  <Input key={index} index={index} autoTab={autoTab} />
));

const App = () => <div className="App">{blocks}</div>;

You have to use ref to focus on next element, you can use index along with your ref name to distinguish element

like this:

this.refs[yourrefname].nextSibling.focus();

and your input element should have ref with unique name

<input ref={yourrefname + index} className="block" key={index} maxLength={1} onKeyUp={autoTab} />

I solved it by creating a simple CSS selector

const autoTab = (e: ChangeEvent<HTMLInputElement>) => {
    if (`${e.target.value.length}` === e.target.getAttribute("maxlength")) {
      var inputs = document.getElementsByClassName("autotab");

      for (let i = 0; i < inputs.length; i++) {
        if (e.target == inputs[i]) {
          i++;
          if (i < inputs.length) {
            let next: any = inputs[i];
            next.focus();
          }
        }
      }
    }
  };

  return (
    <>
      <div>
        <input
          className="autotab"
          type="text"
          maxLength={1}
          onChange={(e) => {
            autoTab(e);
          }}
        />
    </>
  );
Related