Toni Oriol
Toni Oriol

Reputation: 75

Replace subsring by an HTML tag in react

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>
        )
    }
}

Upvotes: 2

Views: 7765

Answers (2)

jered
jered

Reputation: 11581

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.

Upvotes: 3

Danny Delott
Danny Delott

Reputation: 6998

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>
  )
}

Upvotes: 1

Related Questions