Reputation: 3173
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
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