Palaniichuk Dmytro
Palaniichuk Dmytro

Reputation: 3173

highlight searching text on type react

I try to highlight text in a input on typing, I use two method in this case. As I understand I need return on search something like this: <mark>${item}<mark/>

fuzzyContains = (text, search) => {
        debugger
        if (!text)
            return false
        if (!search)
            return true

        search = search.toLowerCase()
        text = text.toString().toLowerCase()

        let previousLetterPosition = -1

        return search.split('').every(s => {
            debugger
            previousLetterPosition = text.indexOf(s, previousLetterPosition + 1)

            return previousLetterPosition !== -1
        })
    }

    handleSearch = search => {
        const {data} = this.state
        debugger
        let filteredData = data.filter(x => Object.keys(x).some(key => {
            debugger
            this.fuzzyContains(x[`<mark>${key}<mark/>`], search)}))

        this.setState({filteredData, search})
    }

Upvotes: 3

Views: 12614

Answers (1)

Chris
Chris

Reputation: 59511

The code you provide is too little to understand what's actually going on in your app. Instead, I have made a simple example below.


In the following snippet I start by creating an application with a fixed text and an input element. The input element has a listener that has an onChange trigger. Whenever you type something, the changeInput function is fired.

The function starts by getting the innerText of the related DOM node where the text is printed. It then tries to find your entered substring with indexOf. If there's a match, we split the string into three pieces (the matching text, and the substrings before and after that matching text, if any).

If there is no match, we reset the text back to the initial value.

The whole thing is then entered into an array; the 1st and 3rd items are plain strings, whereas the 2nd item (the match) is a React Element of type strong which is used to highlight the matching text.

class MyApp extends React.Component {
  constructor() {
    super();
    this.initialText = "Lorem ipsum dolor sit amet";
    this.state = {
      text: this.initialText,
      inputValue: ""
    };
  }
  changeInput = (e) => {
    let value = e.target.value;
    let txt = document.getElementById("myText").innerText;
    let idx = txt.indexOf(value);
    if(idx >= 0) {
      let newText = [txt.substring(0, idx), <strong>{txt.substring(idx, idx + value.length)}</strong>, txt.substring(idx + value.length)];
      this.setState({inputValue: value, text: newText});
    } else {
      this.setState({inputValue: value, text: this.initialText});
    }    
  }
  render() {
    return (
      <div>
        <p id="myText">{this.state.text}</p>
        <input onChange={this.changeInput} value={this.state.inputValue} />
      </div>
    );
  }
}

ReactDOM.render(<MyApp />, document.getElementById("app"));
strong {
  background: red;
  color: white;
  font-weight: inherit;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>


Note that this can be done with using refs if you prefer. If you want to avoid DOM lookups and the use of refs, you could always use 2 state variables; one that holds the resulting JSX and one that holds the plain text representation.

Also note that this will only highlight the first matching substring. For example, if you had the string: "Lorem ipsum, Lorem ipsum", and you searched for "Lorem", only the first occurrence of that word would be highlighted. If you want multiple hightlights, you could try using some kind of regex.

Upvotes: 7

Related Questions