2

How can i surround certain letters in a string by an html tag?

For example, given this string: Sgt. Pepper's Lonely Hearts Club Band and this substring: Pepp. I want to surround the substring inside the string with an html tag. Like this: Sgt. <mark class="Search-suggestions-match">Pepp</mark>er's Lonely Hearts Club Band.

I achieved this, but React is escaping the span tags. But also, i don't know if i should take another approach, maybe a more JSX one.

This is the component where im trying to implement it:

class SearchSuggestions extends Component {

    constructor(props) {
        super(props)

        if (!this.props.suggestions || !this.props.term) {
            return
        }

        this.state = {
            suggestions : this.props.suggestions.map(item => this.markText(item))
        }
    }

    markText(string) {
        return string.replace(new RegExp(this.props.term, "ig"), match => {
            return `<mark class="Search-suggestions-match">${match}</mark>`
        })
    }

    render() {
        if (!this.props.suggestions || !this.props.term) {
            return null
        }
        return (
            <ul className="Search-suggestions-component">
                {this.state.suggestions.map((value, i) => <li key={i}>{value}</li>)}
            </ul>
        )
    }
}

2 Answers 2

4

Use a regular expression to split the string, capturing the desired match, and then format it with JSX:

markText(string) {
    let strArr = string.split(new RegExp(`(${this.props.term})`, "ig"));
    return strArr.map((ea, i) => {
      if(ea.toLowerCase() === this.props.term.toLowerCase()){
        return <mark key={`match${i}`} className="Search-suggestions-match">{ea}</mark>
      } else {
        return ea;
      }
    });
}

HTML inside of a string will NOT get added to the DOM, so you need to use JSX to return an actual React element instead.

Edit: added toLowerCase() so that matches will ignore letter case like they do in the regular expression.

Sign up to request clarification or add additional context in comments.

1 Comment

Be careful -- what if the search term ${this.props.term} contains regex meta-characters? What if I search for pepp.+?
1

Typically you should not pass JSX as a string and expect React to render the element.

However there is an escape hatch that can be used to achieve what you want, check out dangerouslySetInnerHTML

From the docs:

dangerouslySetInnerHTML is React's replacement for using innerHTML in the browser DOM. In general, setting HTML from code is risky because it's easy to inadvertently expose your users to a cross-site scripting (XSS) attack. So, you can set HTML directly from React, but you have to type out dangerouslySetInnerHTML and pass an object with a __html key, to remind yourself that it's dangerous

So you could still do something like this:

render() {

  ...

  return (
    <ul className="Search-suggestions-component">
      {
        this.state.suggestions.map((value, i) => (
          <li key={i} dangerouslySetInnerHTML={{ __html: value }} />
        )
      }
    </ul>
  )
}

3 Comments

With respect, I think that dangerouslySetInnerHTML is not a good to use here. Too risky, especially with something involving user input, and there are plenty of other solutions that don't use this escape hatch.
Agreed, a more proper solution would be to refactor the <li> into a separate component to handle this kind of UI.
Thanks Danny, nice to know this handy option. But I totally agree with @jered.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.