Reputation: 7336
I am building an autocomplete in JavaScript that needs to highlight words when doing a search:
That works fine, but there's an issue with escaped characters.
When trying to highlight a text with escaped characters (for example regex &>< example
), the following is happening:
That's happening because I am doing the following:
element.innerHTML.replace(/a/g, highlight)
function highlight(str) {
return '<span class="foo"' + '>' + str + '</span>';
}
and innerHTML
includes the word &
, so it makes sense.
In conclusion, I need a way to solve that so I would like a function that:
a
and regex <br> example
and returns regex <br> ex<span class="foo">a</span>mple
r
and regex <br> example
and returns <span class="foo">r</span>egex <b<span class="foo">r</span>> example
<
and regex <br> example
and returns regex <span class="foo"><</span>br> example
The entries may or may not contain html blocks, see the issue here (search for <br>
or &
)
Upvotes: 1
Views: 144
Reputation: 10899
str.replace
only returns a new string with the intended replacements. The original string is unchanged.
var str = 'replace me';
var str2 = str.replace(/e/g, 'E');
// For display only
document.write('<pre>' + JSON.stringify({
str: str,
str2: str2
}, null, 2) + '</pre>');
Therefore the code needs to set the returned value from the replace back to the desired element.
Also, innerHTML
will return the escaped text rather than the unescaped text. This could be unescaped itself within the function but why bother if you can use textContent
. However by using innerHTML
when it's time to set the highlighted text to the element it will auto-escape the text for us. :)
UPDATE: the values are passed to the function and then set to the element:
NOTES:
lastIndex
higlightElemById('a', 'regex &>< example', 'a');
higlightElemById('b', 'regex &>< example', '&');
higlightElemById('c', 'regex <br> example', '<');
higlightElemById('d', 'regex <br> example', 'e');
higlightElemById('e', 'regex <br> example', '[aex]');
function higlightElemById(id, str, match) {
var itemElem = document.getElementById(id);
// minimal regexp escape to prevent shenanigans
var safeMatch = match.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
// construct regexp to match highlight text
var regexp = new RegExp('(.*?)(' + safeMatch + ')', 'g');
var text = '';
var lastIndex;
var matches;
while (matches = regexp.exec(str)) {
// Escape the non-matching prefix
text += escapeHTML(matches[1]);
// Highlight the match
text += highlight(matches[2]);
// Cache the lastIndex in case no regexp at end
lastIndex = regexp.lastIndex;
}
if (text) {
text += escapeHTML(str.substr(lastIndex));
} else {
text += escapeHTML(str);
}
itemElem.innerHTML = text;
}
function highlight(str) {
return '<span class="myHighlightClass">' + str + '</span>';
}
function escapeHTML(html) {
this.el = this.el || document.createElement('textarea');
this.el.textContent = html;
return this.el.innerHTML;
}
.myHighlightClass {
text-decoration: underline;
color: red;
}
<div id="a"></div>
<div id="b"></div>
<div id="c"></div>
<div id="d"></div>
<div id="e"></div>
Upvotes: 1