1

In the example below, I'd like to find all strings within delimiters :; and replace them with HTML Mark tags around each match. If it works correctly, those strings should be highlighted in yellow because of the Mark tag. Instead, my tags aren't being expanded. Is there a way to do this in JSX?

function App() {
  let inputStr = 'This is a :test; of :highlighting;'
  return (
    <div>
      {HighlightText(inputStr)}   
    </div>
  );
}

function HighlightText(str) {
  let MyTag = 'Mark';
  var matches = str.split(/[:;]/);
  let outputStr = '';
  matches.forEach(m => {
    const test = ':' + m + ';';
    if (str.includes(test)) {
      outputStr += <MyTag> + m + </MyTag>;
    } else {
      outputStr += m;
    }
  });
  return outputStr;
}

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

2 Answers 2

2

As being said in the comments to another answer, I would avoid dangerouslySetInnerHTML for security considerations.

Instead, you may break your string into array of highlighted and non-highlighted sub-strings and render those either as <mark> or regular <span> elements, respectively:

const { render } = ReactDOM,
      rootNode = document.getElementById('root'),
      
      testString = `This is a :test; of :highlighting;`
      
      const App = () => {
        const subStrings = testString.split(/(:.*?;)/)
        return (
          <div>
            {
              subStrings.map((s,key) => 
                /(:.*;)/.test(s) ? 
                <mark {...{key}}>{s.slice(1,-1)}</mark> :
                <span>{s}</span>
              )
            }
          </div>
        )
      }
      
render (
  <App />,
  rootNode
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>

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

Comments

1

Use the /:([^:;]*);/g with String.prototype.split() to get an array like this:

["This is a ", "test", " of ", "highlighting", ""]

Then you can use Array.prototype.flatMap() to group it like this:

[["This is a ", "test"], [" of ", "highlighting"], [""]]

Then finally you can use Array.prototype.map() to return a <React.Fragment /> for each inner array above:

ReactDOM.render(<App />, document.getElementById('react'));

function App() {
  const inputStr = 'This is a :test; of :highlighting;';

  return (
    <div>{highlightText(inputStr)}</div>
  );
}

function highlightText(str) {
  const MyTag = 'Mark';
  const matches = str.split(/:([^:;]*);/g);
  const groups = matches.flatMap(
    (value, index, array) => (
      index % 2 === 0
      ? [array.slice(index, index + 2)]
      : []
    )
  );

  return groups.map(
    ([prefix, text], index) => (
      <React.Fragment key={index}>
        {prefix} {text && <MyTag>{text}</MyTag>}
      </React.Fragment>
    )
  );
}

// example function demonstrating that `MyTag` resolves to `<Mark />`
function Mark({ children }) {
  return (
    <mark>{children}</mark>
  );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Comments

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.