1

I want to display text message conversations in my chat app.

My case: When user search some text in search box, message include searchText will be style with red color. Example:

user search: "div"

// messageText = "abc <div>all is text</div>" (searching in plain text here);
// htmlText = "abc &lt;div&gt;all is text&lt;/div&gt;";
// OUTPUT MUST BE: "abc &lt;<span style='color: red'>custom</span>&gt;string &lt;<span style='color: red'>custom</span>&gt"

Here is my code:

const origin = "abc <div>all is text </div>"; // text input
const str = "abc &lt;div&gt;all is text &lt;/div&gt;"; // after sanitize

const element = document.createElement("div");
element.innerHTML = str;
for (let node of element.childNodes) {
  // I want replace "div" text with my custom html
  node.textContent = node.textContent.replace(/div/gi, "<span style='color: red'>custom</span>");
}

const result = element.innerHTML;
console.log(result);

Here is the result output

Output

abc &lt;&lt;span style='color: red'&gt;custom&lt;/span&gt;&gt;string &lt;/&lt;span style='color: red'&gt;custom&lt;/span&gt;&gt;

Expect

abc &lt;<span style='color: red'>custom</span>&gt;string &lt;<span style='color: red'>custom</span>&gt;

Can you help me, tks for your help.

Phát Nguyễn
  • 155
  • 2
  • 16

2 Answers2

2

node.textContent = ... will produce valid text, by escaping whatever you pass to it.

If you want to insert HTML instead, use node.innerHTML

node.innerHTML = node.textContent.replace(/div/gi, "<span style='color: red'>custom</span>");

Edit I realize your problem is more complex than that. You first need to escape HTML inside your text, and then replace div with the HTML you want to insert, and finally use inneHTML to apply the result.

Edit2 After updating your question, I understand you want to search/highlight something within text input. Edited the code to do that

As per this answer:

function escapeHtml(unsafe) {
  return unsafe
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;");
}

function highlightSearch(node, query) {
  //Within node, highlight matched text (using replacement "$&")
  //highlight done by surrounding found text with a <span>
  let txt = node.textContent;                 //Get inner text
  console.log('raw text:', txt);
  txt = escapeHtml(txt);                      //Escape text with HTML entities
  console.log('html text:', txt);
  //Search and replace ("$&" = whole matched substring)
  txt = txt.replaceAll(query, "<span style='color: red'>$&</span>");
  console.log('highlighted:', txt);
  //Show result
  node.innerHTML = txt;         //<-- innerHTML
}

document.querySelector('#btn-search').addEventListener('click', e => {
  //Read query
  let q = document.querySelector('#search-query').value;
  console.log('raw query:', q);
  //Make search query html
  q = escapeHtml(q);
  console.log('html query:', q);
  //Perform search/highlight
  document.querySelectorAll('.search-in').forEach(node => {
    highlightSearch(node, q);
  });
});
button {
  margin-top: 1em;
}
<p class="search-in">abc &lt;div&gt;all is text &lt;/div&gt;</p>
<p class="search-in">a &lt;div> is not a &lt;/div>. but I can find 2 &lt;div></p>
<input type="search" id="search-query" value="<div>" />
<button id="btn-search">Search</button>
julien.giband
  • 2,467
  • 1
  • 12
  • 19
  • Tk, for your answer, but "div" just is example, it can be everything in `origin` ( const origin = "abc
    all is text
    "; // text input ) If I replace "div" with "
    " your code will fail.
    – Phát Nguyễn May 17 '21 at 08:08
  • I don't understand what you mean. I responded to your question as it is, with the example you gave. If you have different cases, then you should edit your question to explicit your possible input/output. It's probably just a matter of changing the regex in there – julien.giband May 17 '21 at 08:33
  • thank you. I edit for my case, you can check it. – Phát Nguyễn May 17 '21 at 08:48
  • There you've got it – julien.giband May 17 '21 at 09:16
  • I've refactored the code to optimize it and make it clearer by breaking it into functions. I also changed the way I made the search Regexp `global` and `ignoreCase` as it didn't seem to work. – julien.giband May 17 '21 at 09:50
  • And removed all the RegExp complexity using method `String.replaceAll`. I'm not touching it again. Promised. – julien.giband May 17 '21 at 10:13
  • FYI, for highlighting text, you should use a `` element instead of `` – julien.giband May 17 '21 at 10:16
0

Maybe you don't need to sanitize:

const origin = document.querySelector("#inp").value;
const tmp = Object.assign( document.createElement("div"), {innerHTML: origin} );
tmp.querySelectorAll("div").forEach( el => {
  const left = `<span style="color:red">&lt;custom&gt;</span>`;
  const right = `<span style="color:red">&lt;/custom&gt;</span>`;
  const nwSpan = Object.assign( 
    document.createElement("span"),
    {innerHTML: `${left}${el.innerHTML}${right}`} );
  el.parentNode.replaceChild(nwSpan, el);
} );
console.log(tmp.innerHTML);
document.querySelector("#result").innerHTML = tmp.innerHTML;
<input type="text" id="inp" value="abc <div>all is text </div>">

<pre id="result"></pre>
KooiInc
  • 119,216
  • 31
  • 141
  • 177