How to highlight text based on user input with React?

Viewed 1803

Let's take the simple case of a barebones editable <div> with contenteditable="true":

<div contenteditable="true">
  Edit this 
</div>

The user is able to input/edit/delete the contents of this <div> element.

For certain input forms, if the user inputs text greater than some limit of characters, the characters added over this limit will be highlighted (warning the user to stop adding text).

How could I achieve something like this with React?

I think <span> could be used to highlight the text, but I cannot conceptualize how to begin the highlighting after the character count limit has been reached.

Please let me know if the question is clear. Thank you for the help.

1 Answers

You could reach by checking in the onchange event, the div text length and wrap extra text by a span and set inner html of that div again by concatenatin text (without extra chars) + wrapped extra text span;

(its not recommended to use innerHTML due to xss injection but it's only solution I've found to this problem )

that main problem here is once setting div innerHTML , your cursor will rest to start of div text , so i've done a little trick to set selection at the end of the text using

       // get textt par before extra text
      let start =  html.slice(0, MAX_LENGTH ) ;
      // get extra text
      let overflow =  html.slice(MAX_LENGTH) ;
      // rap extra text by span
      overflow = `<span style="color:${COLOR}">${overflow}</span>`;
      //set text as innerHTML (or use dangerouslyINerHTML with sate var)
      ref.current.innerHTML = start+overflow
      
      // below part is to set cursor , at the end after inner html 
      // because innerHTML will reset selection to the start of div text
      let range = document.createRange()
      var sel = window.getSelection()
      range.setStart(ref.current.lastChild, 1   )

      sel.removeAllRanges()
      sel.addRange(range)

See here working snnipet with deiferent props example created a component and use different props

const { useRef } = React;

/* create Eitable Component */
const EditAbleDiv  =( props ) => {
  // get max length from props
  const MAX_LENGTH = props.maxLength || 40;
  // get color from props
  const COLOR = props.warningColor || 'orange';
  
  let ref = useRef(null);
  
  
  // on change event
  const contentChange = (e) => {
    
    // get only text without html tags
    let html = e.target.innerText;
    
    if (html.length > MAX_LENGTH) {

      // get textt par before extra text
      let start =  html.slice(0, MAX_LENGTH ) ;
      // get extra text
      let overflow =  html.slice(MAX_LENGTH) ;
      // rap extra text by span
      overflow = `<span style="color:${COLOR}">${overflow}</span>`;
      //set text as innerHTML (or use dangerouslyINerHTML with sate var)
      ref.current.innerHTML = start+overflow
      
      // below part is to set cursor , at the end after inner html 
      // because innerHTML will reset selection to the start of div text
      let range = document.createRange()
      var sel = window.getSelection()
      range.setStart(ref.current.lastChild, 1   )

      sel.removeAllRanges()
      sel.addRange(range)

    }
  }

  return <div  ref={ref}
  contentEditable 
  onInput={contentChange} 
  >
    Edit text
</div>
}
/* end component */


function App() {
  
  return (
    <div>
      <b> click in below div to edit text </b> <br/><br/>
      <EditAbleDiv maxLength={25} />
      <hr />
      <EditAbleDiv maxLength={18} warningColor={"red"} />
      <hr />
      <EditAbleDiv maxLength={12} warningColor={"green"} />
    </div>
  )
}

ReactDOM.render(
    <App />,
    document.body
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

Related