1

My main objective is to indent the space within the text area by a particular amount once I click the Tab button.

I am using React JS and Bootstrap. I have created a bootstrap text area in the render function like so.

 <textarea class="col-12 form-control-lg" id="exampleFormControlTextarea1"placeholder="Write some Lyrics" rows="50" onKeyDown={this.useTab()} value={this.state.lyrics}
     onChange={e => this.setState({ lyrics : e.target.value },()=>{
         this.updateSongs("Body")
       })}
   </textarea>

Outside my render function I am running the useTab() method.

useTab(e) {
    console.log(e.keyCode); //press TAB and get the keyCode
    }

I get the below error on running this code.

Unhandled Rejection (TypeError): Cannot read property 'keyCode' of undefined

I did refer to the below solution but was still unable to fix the error.

ReactJS handle tab character in textarea

Do I have to bind the function in the constructor? I'm not really sure why the event is not being captured and what I seem to be missing here.

blaziken105
  • 501
  • 3
  • 10
  • 25
  • you can watch for keyboard input and if the keycode is of tab then you can simply insert the spaces at the place of cursor inside the textarea. – anees May 02 '20 at 18:36
  • I think you should change the arrow function and use a normal `function()` expression. – Dave Pastor May 02 '20 at 18:37

2 Answers2

2

Please follow this example:

class App extends React.Component {
  constructor() {
    super();
    this.textAreaRef = React.createRef();
    this.state = {
      lyrics: ""
    };
  }

  onChange = event => {
    this.setState({
      lyrics: event.target.value
    });
  };

  // Using arrow function so no need to bind 'this'
  onKeyDown = event => {
    // 'event.key' will return the key as a string: 'Tab'
    // 'event.keyCode' will return the key code as a number: Tab = '9'
    // You can use either of them
    if (event.keyCode === 9) {
      // Prevent the default action to not lose focus when tab
      event.preventDefault();

      // Get the cursor position
      const { selectionStart, selectionEnd } = event.target;
      // update the state
      this.setState(
        prevState => ({
          lyrics:
            prevState.lyrics.substring(0, selectionStart) +
            "\t" + // '\t' = tab, size can be change by CSS
            prevState.lyrics.substring(selectionEnd)
        }),
        // update the cursor position after the state is updated
        () => {
          this.textAreaRef.current.selectionStart = this.textAreaRef.current.selectionEnd =
            selectionStart + 1;
        }
      );
    }
  };

  render() {
    return (
      <div className="App">
        <textarea
          ref={this.textAreaRef}
          className="col-12 form-control-lg"
          placeholder="Write some Lyrics"
          rows="50"
          value={this.state.lyrics}
          onChange={this.onChange}
          onKeyDown={this.onKeyDown}
        />
      </div>
    );
  }
}
ReactDOM.render( <App /> , document.getElementById('root'))
.App {
  padding: 0 .5rem;
}

/* control tab size */
textarea {
  tab-size: 8; 
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
awran5
  • 4,333
  • 2
  • 15
  • 32
0

You need to add an event listener for your useTab function:

document.addEventListener('keydown', useTab);

Edit:

Instead of an event listener, you can use the onKeyDown handler in your input

useTab = (event) => {
    event.preventDefault();
    console.log(event.key)
}
render: function(){
     return(
         <div>
           <input onKeyDown={this.useTab} />
        </div>
     );
}
Ibraheem Ahmed
  • 11,652
  • 2
  • 48
  • 54