I want to display text to HTML by a JavaScript function. How can I escape HTML special characters in JavaScript? Is there an API?
-
19This is not a duplicate, since this question does not asks about jQuery. I am interested only in this one, since I do not use jQuery... – lvella Aug 07 '13 at 16:08
-
6possible duplicate of [HtmlSpecialChars equivalent in Javascript?](http://stackoverflow.com/questions/1787322/htmlspecialchars-equivalent-in-javascript) – Bergi Aug 07 '13 at 16:19
-
Note that the browsers are working on a [new HTML Sanitizer API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Sanitizer_API). – Flimm Jan 26 '22 at 20:05
17 Answers
Here's a solution that will work in practically every web browser:
function escapeHtml(unsafe)
{
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
If you only support modern web browsers (2020+), then you can use the new replaceAll function:
const escapeHtml = (unsafe) => {
return unsafe.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll("'", ''');
}
-
19
-
57because: http://stackoverflow.com/questions/2083754/why-shouldnt-apos-be-used-to-escape-single-quotes – Shreyans Mar 27 '13 at 21:33
-
3I think regular expressions in `replace()` calls are unnecessary. Plain old single-character strings would do just as well. – jamix May 30 '14 at 14:47
-
1don't forget about .replace(/ /g, ' '), if you convert text with space indentations, spaces may be lost. – Stepan Yakovenko Jul 04 '15 at 07:32
-
1Possibly it's useful to test whether 'unsafe' is a string. It's not unlikely for UI code to deal with numbers. – Gerard Nov 23 '16 at 16:26
-
I had an issue with ".replace(/'/g, "'");", browser was converting it back to apostrophe in the inline javascript. Workaround was to add escape characther: .replace(/'/g, "\\'"); – soulrider Sep 28 '18 at 18:52
-
2@StepanYakovenko That's better handled with CSS. As it is, replacing every space with ` ` will prevent text breaks on spaces (` ` means "non-breaking space"). – Roy Tinker Aug 11 '19 at 03:42
-
36
-
4[' is valid in HTML5 but not in HTML4](https://stackoverflow.com/questions/2083754/why-shouldnt-apos-be-used-to-escape-single-quotes/18551371#18551371) – Hank W Sep 17 '20 at 16:13
-
That's the only characters you *need* to escape? I'd thought it would be larger, but whatever... – Reality Apr 11 '21 at 20:12
-
1@Reality OWASP recommends you escape more than just those, I'm not sure why, but I think it's for futureproofing and some old browsers. This will likely work in most cases, but you can see my answer shows the full character range to escape. – ADJenks Apr 30 '21 at 21:46
-
In case anyone wants to use in javascript strings and not in html then better to use unicode characters for example less than and greater than symbols can use var lt = '\u003c' and gt = '\u003e', here is another reference: https://stackoverflow.com/questions/13093126/insert-unicode-character-into-javascript/13093138 – Khuram Niaz Jan 07 '22 at 23:52
-
2@jamix No, if you do that it only replaces the first occurrence of `&`, first occurrence of `<`, etc. You need to use regexes with the `/g` flag. – David Knipe Mar 02 '22 at 12:05
function escapeHtml(html){
var text = document.createTextNode(html);
var p = document.createElement('p');
p.appendChild(text);
return p.innerHTML;
}
// Escape while typing & print result
document.querySelector('input').addEventListener('input', e => {
console.clear();
console.log( escapeHtml(e.target.value) );
});
<input style='width:90%; padding:6px;' placeholder='<b>cool</b>'>

- 118,978
- 58
- 307
- 400

- 1,539
- 15
- 10
-
-
9Note that this doesn't escape quotes (`"` or `'`) so strings from this function can still do damage if they are used in HTML tag attributes. – jdgregson Apr 30 '21 at 19:32
You can use jQuery's .text()
function.
For example:
From the jQuery documentation regarding the .text()
function:
We need to be aware that this method escapes the string provided as necessary so that it will render correctly in HTML. To do so, it calls the DOM method .createTextNode(), does not interpret the string as HTML.
Previous Versions of the jQuery Documentation worded it this way (emphasis added):
We need to be aware that this method escapes the string provided as necessary so that it will render correctly in HTML. To do so, it calls the DOM method .createTextNode(), which replaces special characters with their HTML entity equivalents (such as &lt; for <).

- 18,439
- 2
- 38
- 52

- 7,033
- 5
- 33
- 37
-
9You can even use it on a fresh element if you just want to convert like this: `const str = "foo<>'\"&";` `$('').text(str).html()` yields `foo<>'"&`– amoebe Nov 14 '17 at 21:46
-
3Note that this leaves quotes `'` and `"` unescaped, which may trip you up – Ben Philipp Sep 04 '21 at 08:23

- 30,738
- 21
- 105
- 131

- 5,287
- 1
- 29
- 28
-
what is the opposite of this? name of the function that does the opposite of this? – Sunil Garg Jan 06 '20 at 07:04
-
6Same functions in underscore: https://underscorejs.org/#escape & https://underscorejs.org/#unescape – juanmirocks May 21 '20 at 10:38
-
Doesn't seem to work for IP addresses when you try `_.escape(192.168.1.1)`, but if I add quotes, then it works: `_.escape('52.60.62.147')` even though I'm referencing a variable where the value is not a string. LoDash is so great! – Kreidol Sep 07 '22 at 02:23
This is, by far, the fastest way I have seen it done. Plus, it does it all without adding, removing, or changing elements on the page.
function escapeHTML(unsafeText) {
let div = document.createElement('div');
div.innerText = unsafeText;
return div.innerHTML;
}

- 659
- 5
- 10
-
18Warning: it *does not* escape quotes so you can't use the output inside attribute values in HTML code. E.g. `var divCode = 'Div content'` will yield invalid HTML! – izogfif Jul 17 '19 at 11:59
-
1Using [div.textContent](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) instead of [div.innerText](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText) would probably be more idiomatic. – Klesun Nov 27 '21 at 20:16
-
Just wondering, would repeatedly calling this eventually leave document full of extra div elements? Or does it get garbage collected? – Magnus Feb 01 '22 at 13:16
-
@Magnus The div isn't attached to the DOM, so [it will eventually be garbage collected](https://stackoverflow.com/questions/35912302/when-will-a-local-unattached-dom-element-be-garbage-collected). So no, this will not fill the document with useless elements. – Michael T Jul 14 '22 at 11:33
I think I found the proper way to do it...
// Create a DOM Text node:
var text_node = document.createTextNode(unescaped_text);
// Get the HTML element where you want to insert the text into:
var elem = document.getElementById('msg_span');
// Optional: clear its old contents
//elem.innerHTML = '';
// Append the text node into it:
elem.appendChild(text_node);

- 12,754
- 11
- 54
- 106
-
I learnt something new about HTML today. https://www.w3schools.com/jsref/met_document_createtextnode.asp. – Sellorio Jun 27 '18 at 22:39
-
4Be aware that the content of the text node is not escaped if you try to access it like this: `document.createTextNode("").textContent` – maechler Mar 14 '19 at 15:02
-
This is the correct way if all you're doing is setting text. That's also textContent but apparently it's not well supported. This won't work however if you're building up a string with some parts text some html, then you need to still escape. – jgmjgm Oct 16 '19 at 10:29
-
I really like this, because it's using the DOM properly. It feels less "hacky" than most of the other options. – TRiG Jun 18 '21 at 15:14
It was interesting to find a better solution:
var escapeHTML = function(unsafe) {
return unsafe.replace(/[&<"']/g, function(m) {
switch (m) {
case '&':
return '&';
case '<':
return '<';
case '"':
return '"';
default:
return ''';
}
});
};
I do not parse >
because it does not break XML/HTML code in the result.
Here are the benchmarks: http://jsperf.com/regexpairs
Also, I created a universal escape
function: http://jsperf.com/regexpairs2
-
1It's interesting to see that using the switch is significantly faster than the map. I didn't expect this! Thanks for sharing! – Peter T. Jun 16 '17 at 20:35
-
1There are many many more unicode characters than you could possible code & take into account. I wouldn't recommend this manual method at all. – vsync Jun 13 '18 at 13:31
-
Why would you escape multi-byte characters at all? Just use UTF-8 everywhere. – Neonit Apr 20 '19 at 14:40
-
6Skipping > can potentially break code. You must keep in mind that inside the <> is also html. In that case skipping > will break. If you're only escaping for between tags then you probably only need escape < and &. – jgmjgm Oct 16 '19 at 10:32
The most concise and performant way to display unencoded text is to use textContent
property.
Faster than using innerHTML
. And that's without taking into account escaping overhead.
document.body.textContent = 'a <b> c </b>';

- 23,260
- 9
- 113
- 101
-
@ZzZombo, it is completely normal that it doesn't work with style and script tags. When you add content to them, you add *code*, not *text*, use innerHTML in this case. Moreover, you don't need to escape it, these are two special tags that are not parsed as HTML. When parsing, their content is treated as text until the closing sequence `` is met. – user Dec 25 '17 at 16:47
By the books
When editing HTML attributes use recommended "HTML Attribute Encoding":
OWASP recommends that "[e]xcept for alphanumeric characters, [you should] escape all characters with ASCII values less than 256 with the &#xHH;
format (or a named entity if available) to prevent switching out of [an] attribute."
So here's a function that does that, with a usage example:
function escapeHTML(unsafe) {
return unsafe.replace(
/[\u0000-\u002F\u003A-\u0040\u005B-\u0060\u007B-\u00FF]/g,
c => '&#' + ('000' + c.charCodeAt(0)).slice(-4) + ';'
)
}
document.querySelector('div').innerHTML =
'<span class=' +
escapeHTML('"fakeclass" onclick="alert("test")') +
'>' +
escapeHTML('<script>alert("inspect the attributes")\u003C/script>') +
'</span>'
<div></div>
You should verify the entity ranges I have provided to validate the safety of the function yourself. You could also use this regular expression which has better readability and should cover the same character codes, but is about 10% less performant in my browser:
/(?![0-9A-Za-z])[\u0000-\u00FF]/g
When editing HTML content between <tags>
, use "HTML Entity Encoding":
For this, OWASP recommends you to "look at the .textContent attribute as it is a Safe Sink and will automatically HTML Entity Encode."

- 2,973
- 27
- 38
DOM Elements support converting text to HTML by assigning to innerText. innerText is not a function but assigning to it works as if the text were escaped.
document.querySelectorAll('#id')[0].innerText = 'unsafe " String >><>';

- 6,505
- 2
- 30
- 24
-
1At least in Chrome assigning multiline text adds `
` elements in place of newlines, that can break certain elements, like styles or scripts. The `createTextNode` is not prone to this problem. – ZzZombo Dec 25 '17 at 04:30 -
1`innerText` has some legacy/spec issues. Better to use `textContent`. – Roy Tinker Aug 11 '19 at 03:35
You can encode every character in your string:
function encode(e){return e.replace(/[^]/g,function(e){return"&#"+e.charCodeAt(0)+";"})}
Or just target the main characters to worry about (&, inebreaks, <, >, " and ') like:
function encode(r){
return r.replace(/[\x26\x0A\<>'"]/g,function(r){return"&#"+r.charCodeAt(0)+";"})
}
test.value=encode('How to encode\nonly html tags &<>\'" nice & fast!');
/*************
* \x26 is &ersand (it has to be first),
* \x0A is newline,
*************/
<textarea id=test rows="9" cols="55">www.WHAK.com</textarea>

- 923
- 9
- 6
-
Writing your own escape function is generally a bad idea. Other answers are better in this regard. – jannis Oct 13 '16 at 12:29
If you already use modules in your application, you can use escape-html module.
import escapeHtml from 'escape-html';
const unsafeString = '<script>alert("XSS");</script>';
const safeString = escapeHtml(unsafeString);

- 30,738
- 21
- 105
- 131

- 4,048
- 2
- 29
- 34
I came across this issue when building a DOM structure. This question helped me solve it. I wanted to use a double chevron as a path separator, but appending a new text node directly resulted in the escaped character code showing, rather than the character itself:
var _div = document.createElement('div');
var _separator = document.createTextNode('»');
//_div.appendChild(_separator); /* This resulted in '»' being displayed */
_div.innerHTML = _separator.textContent; /* This was key */

- 30,738
- 21
- 105
- 131

- 11
- 1
Just write the code in between <pre><code class="html-escape">....</code></pre>
. Make sure you add the class name in the code tag. It will escape all the HTML snippet written in
<pre><code class="html-escape">....</code></pre>
.
const escape = {
'"': '"',
'&': '&',
'<': '<',
'>': '>',
}
const codeWrappers = document.querySelectorAll('.html-escape')
if (codeWrappers.length > 0) {
codeWrappers.forEach(code => {
const htmlCode = code.innerHTML
const escapeString = htmlCode.replace(/"|&|<|>/g, function (matched) {
return escape[matched];
});
code.innerHTML = escapeString
})
}
<pre>
<code class="language-html html-escape">
<div class="card">
<div class="card-header-img" style="background-image: url('/assets/card-sample.png');"></div>
<div class="card-body">
<p class="card-title">Card Title</p>
<p class="card-subtitle">Srcondary text</p>
<p class="card-text">Greyhound divisively hello coldly wonderfully marginally far upon
excluding.</p>
<button class="btn">Go to </button>
<button class="btn btn-outline">Go to </button>
</div>
</div>
</code>
</pre>

- 128
- 3
- 5
Use this to remove HTML tags from a string in JavaScript:
const strippedString = htmlString.replace(/(<([^>]+)>)/gi, "");
console.log(strippedString);

- 30,738
- 21
- 105
- 131

- 43
- 1
-
9This requires the "prototype.js" library, which wasn't immediately apparent from the demo. :( – audiodude Aug 01 '14 at 19:56
I came up with this solution.
Let's assume that we want to add some HTML to the element with unsafe data from the user or database.
var unsafe = 'some unsafe data like <script>alert("oops");</script> here';
var html = '';
html += '<div>';
html += '<p>' + unsafe + '</p>';
html += '</div>';
element.html(html);
It's unsafe against XSS attacks. Now add this: $(document.createElement('div')).html(unsafe).text();
So it is
var unsafe = 'some unsafe data like <script>alert("oops");</script> here';
var html = '';
html += '<div>';
html += '<p>' + $(document.createElement('div')).html(unsafe).text(); + '</p>';
html += '</div>';
element.html(html);
To me this is much easier than using .replace()
and it'll remove!!! all possible HTML tags (I hope).

- 30,738
- 21
- 105
- 131

- 1,792
- 2
- 16
- 21
-
this is dangerous idea, it parses the unsafe HTML String as HTML, if the element were attached to the DOM it would exeute. use .innerText instead. – teknopaul Aug 21 '17 at 10:21
-