{"id":17269,"date":"2022-09-21T07:26:31","date_gmt":"2022-09-21T14:26:31","guid":{"rendered":"https:\/\/coderpad.io\/?p=17269"},"modified":"2023-06-05T14:05:11","modified_gmt":"2023-06-05T21:05:11","slug":"javascript-innerhtml","status":"publish","type":"post","link":"https:\/\/coderpad.io\/blog\/development\/javascript-innerhtml\/","title":{"rendered":"How to Use innerHTML in JavaScript, Plus Alternate Methods"},"content":{"rendered":"\n<p>If you&#8217;re creating any dynamic web page these days, making sure you have relevant and up-to-date information is vital.<\/p>\n\n\n\n<p>Whether it&#8217;s showing stock quotes, the current time, the user&#8217;s name, or any other piece of info that changes with time or user, you&#8217;ll want to make sure you&#8217;re regularly updating this dynamic content to produce the best experience for your users.<\/p>\n\n\n\n<p>Luckily for you, we can associate these dynamic pieces of information with HTML elements and easily display their current state using the <em>innerHTML <\/em>property.<\/p>\n\n\n\n<p>This blog post will walk you through the pros and cons of using the <code>innerHTML<\/code> property and how you can use it safely without opening the doors to potential <a href=\"https:\/\/owasp.org\/www-community\/attacks\/xss\/\" target=\"_blank\" rel=\"noreferrer noopener\">Cross-Site scripting (XSS) attacks<\/a>.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What is innerHTML in JavaScript?<\/strong><\/h2>\n\n\n\n<p><code>innerHTML<\/code> is an HTML element property that has two uses for web developers:<\/p>\n\n\n\n<p>1) You can use it to get the internal HTML content of any HTML element as an HTML string.&nbsp;<\/p>\n\n\n\n<p>2) You can also use it to set or change elements&#8217; <code>innerHTML<\/code> content.&nbsp;<\/p>\n\n\n\n<p>Take the following HTML example:&nbsp;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\u201dsomeDivElement\u201d<\/span>&gt;<\/span>\n\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>Hello World<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>To access the innerHTML you\u2019ll first need to access the element itself with <code>getElementById()<\/code>:&nbsp;<\/p>\n\n\n\n<p><code>const someDivElement = document.getElementById(\u201csomeDivElement\u201d);<\/code><\/p>\n\n\n\n<p>Then we can access the <code>innerHTML<\/code>:&nbsp;<\/p>\n\n\n\n<p><code>console.log(someDivElement.innerHTML);<\/code><\/p>\n\n\n\n<p>This will give us <code><strong>\"<\/strong>&lt;span&gt;Hello World&lt;\/span&gt;<strong>\"<\/strong><\/code><strong> <\/strong>as a string. And if you want to modify the <code>innerHTML<\/code> property of the element, it can be achieved in the following way:&nbsp;<\/p>\n\n\n\n<p><code>someDivElement.innerHTML = \u201c&lt;span&gt;Something just like this\u2026&lt;\/span&gt;\u201d;<\/code><\/p>\n\n\n\n<p>Here&#8217;s an example that can be used by to-do apps where list items are appended to an existing list:&nbsp;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">const<\/span> todos = &#91;\n  <span class=\"hljs-string\">\"Exercise\"<\/span>,\n  <span class=\"hljs-string\">\"Have milk\"<\/span>,\n  <span class=\"hljs-string\">\"Buy bread\"<\/span>,\n  <span class=\"hljs-string\">\"Walk the dog\"<\/span>,\n  <span class=\"hljs-string\">\"Sleep timely :)\"<\/span>\n];\n\n<span class=\"hljs-keyword\">const<\/span> todoListPreview = <span class=\"hljs-built_in\">document<\/span>.getElementById(\u201ctdlst-preview\u201d); <span class=\"hljs-comment\">\/* returns an &lt;ol&gt; *\/<\/span> element\n\ntodos.forEach(<span class=\"hljs-function\">(<span class=\"hljs-params\">todo<\/span>) =&gt;<\/span> {\n  todoListPreview.innerHTML += <span class=\"hljs-string\">`&lt;li&gt;<span class=\"hljs-subst\">${todo}<\/span>&lt;\/li&gt;`<\/span>;\n});\n\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This will add all the to-do items as <code>&lt;li&gt;<\/code>tags in our ordered list element. These are the ways we can get and set the <code>innerHTML<\/code> properties.&nbsp;<\/p>\n\n\n\n<p>However \u2013 as with any piece of code \u2013 there can be exceptions. A common one you may see with <code>innerHTML<\/code> is the<code>SyntaxError<\/code><strong>, <\/strong>which is thrown when the provided HTML string is ill-formed. Here&#8217;s an example:&nbsp;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">someDivElement.innerHTML = \u201c<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>William Bradley \"Brad\" Pitt<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\u201d<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The moment the browser parses this script, <code><strong>\"<\/strong>Brad<strong>\"<\/strong><\/code> is perceived as an unknown identifier because of the double quotes, and the <code>SyntaxError<\/code> exception is thrown.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>When not to use innerHTML<\/strong><\/h2>\n\n\n\n<p>Using <code>innerHTML<\/code> is fine if you&#8217;re using it to get the value of an element&#8217;s <code>innerHTML<\/code> content. Things change, however, if it&#8217;s used to set values, as it accepts <strong>all <\/strong>HTML tags, including the <code>script<\/code> tag. This means you could potentially open up a portal to your cookies and users&#8217; personal information via a Cross Site Scripting (XSS) attack.<\/p>\n\n\n\n<p>An XSS attack is a type of web security vulnerability that allows an attacker to inject malicious code into a webpage. Then that code is executed by the web browser when an unsuspecting user visits the page.&nbsp;<\/p>\n\n\n\n<p>Take the example below that allows a bad script to send a user&#8217;s cookies to its server, which then can be used to impersonate the actual user and perform malicious actions:&nbsp;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">document.getElementById(\u201csomeLogoutButton\u201d).innerHTML = \u201c<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span>&gt;<\/span><span class=\"javascript\">callHome(<span class=\"hljs-built_in\">document<\/span>.cookie);<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\u201d;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Fortunately, the World Wide Web Consortium (W3C) has since released a standard on <a href=\"https:\/\/www.w3.org\/TR\/2008\/WD-html5-20080610\/dom.html\" target=\"_blank\" rel=\"noreferrer noopener\">dynamic markup insertion in HTML<\/a>that states that <code>script<\/code> elements are not executed when inserted using <code>innerHTML<\/code>. However, hackers have found other ways to carry out XSS attacks.&nbsp;<\/p>\n\n\n\n<p>Let&#8217;s take the following example of the <code>img<\/code><strong> <\/strong>tag. It has an <code>onerror<\/code> attribute that accepts JavaScript and is allowed to be run.&nbsp;<\/p>\n\n\n\n<p><code>&lt;img src=123 onerror=alert(\u201cHaha!\u201d)&gt;<\/code><\/p>\n\n\n\n<p>Once browsers parse this tag, the value of <code>src<\/code> is invalid as browsers expect a URL. Thus, an error will be thrown, and as the image tag has an <code>onerror<\/code> listener, the script inside will be executed.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>How to use innerHTML without creating XSS vulnerabilities<\/strong><\/h3>\n\n\n\n<p>To prevent XSS issues, you always want to sanitize your user input, especially when it&#8217;s going to be rendered as-is.&nbsp;<\/p>\n\n\n\n<p>Say you&#8217;re building a <a href=\"https:\/\/en.wikipedia.org\/wiki\/WYSIWYG\" target=\"_blank\" rel=\"noreferrer noopener\">WYSIWYG<\/a> editor in which you&#8217;re letting users save their HTML content in your database via innerHTML. Since you can&#8217;t trust your users&#8217; input, you&#8217;ll need to implement some sanitization to prevent them from adding malicious code.<\/p>\n\n\n\n<p>There are a lot of <a href=\"https:\/\/coderpad.io\/blog\/development\/10-javascript-data-visualization-libs\/\" target=\"_blank\" rel=\"noreferrer noopener\">open-source libraries<\/a> available that provide this sanitization service. One of my favorites is <a href=\"https:\/\/www.npmjs.com\/package\/sanitize-html\" target=\"_blank\" rel=\"noreferrer noopener\">sanitize-HTML<\/a>, which provides HTML cleaning for browser and node environments.<\/p>\n\n\n\n<p><code>npm install sanitize-HTML<\/code><\/p>\n\n\n\n<p>The library is fairly easy to use. Here are a few examples:&nbsp;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ the es module way<\/span>\n<span class=\"hljs-keyword\">import<\/span> sanitizeHtml <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'sanitize-html'<\/span>;\n<span class=\"hljs-comment\">\/\/ the commonjs way<\/span>\n<span class=\"hljs-keyword\">const<\/span> sanitizeHtml = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'sanitize-html'<\/span>);\n\nsanitizeHtml(<span class=\"hljs-string\">'&lt;img src=x onerror=alert(1)\/\/&gt;'<\/span>); <span class=\"hljs-comment\">\/\/ returns \"\"<\/span>\n\nsanitizeHtml(<span class=\"hljs-string\">'&lt;svg&gt;&lt;g\/onload=alert(2)\/\/&lt;p&gt;'<\/span>); <span class=\"hljs-comment\">\/\/ returns \"\"<\/span>\n\nsanitizeHtml(<span class=\"hljs-string\">'&lt;p&gt;abc&lt;iframe\/\/src=jAva&amp;Tab);script:alert(3)&gt;def&lt;\/p&gt;'<\/span>); <span class=\"hljs-comment\">\/\/ returns &lt;p&gt;abcdef&lt;\/p&gt;<\/span>\n\nsanitizeHtml(<span class=\"hljs-string\">'&lt;TABLE&gt;&lt;tr&gt;&lt;td&gt;HELLO&lt;\/tr&gt;&lt;\/TABL&gt;'<\/span>); <span class=\"hljs-comment\">\/\/ returns \"&lt;table&gt;&lt;tr&gt;&lt;td&gt;HELLO&lt;\/td&gt;&lt;\/tr&gt;&lt;\/table&gt;\"<\/span>\n\nsanitizeHtml(<span class=\"hljs-string\">'&lt;UL&gt;&lt;li&gt;&lt;A HREF=\/\/google.com&gt;click&lt;\/UL&gt;'<\/span>); <span class=\"hljs-comment\">\/\/ returns \"&lt;ul&gt;&lt;li&gt;&lt;a href=\\\"\/\/google.com\\\"&gt;click&lt;\/a&gt;&lt;\/li&gt;&lt;\/ul&gt;\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><em>NOTE: You can try the library out at <\/em><a href=\"https:\/\/npm.runkit.com\/sanitize-html\" target=\"_blank\" rel=\"noreferrer noopener\"><em>RunKit<\/em><\/a><em>.<\/em>&nbsp;<\/p>\n\n\n\n<p>By default, sanitize-HTML has a predefined set of rules on what tags and attributes to allow and remove\/escape; however, it can be tailored (<a href=\"https:\/\/www.npmjs.com\/package\/sanitize-html\" target=\"_blank\" rel=\"noreferrer noopener\">refer to the documentation<\/a>) further per the developers&#8217; requirements.&nbsp;<\/p>\n\n\n\n<blockquote class=\"wp-block-quote\"><p>\u26a0\ufe0f Always sanitize HTML string inputs on both the front and back end to reduce the attack vector substantially.&nbsp;<\/p><\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>innerHTML vs. createElement<\/strong><\/h2>\n\n\n\n<p>createElement is an alternative method of innerHTML, so here we&#8217;ll look at the two.&nbsp;<\/p>\n\n\n\n<p><code>createElement<\/code> is faster, as browsers are not required to parse the HTML string and then build a node tree out of it; it also doesn&#8217;t have to attach event listeners as <code>innerHTML<\/code> does. Using <code>innerHTML<\/code> will cause browsers to reparse and recreate all DOM nodes inside the element whose <code>innerHTML<\/code> is modified.&nbsp;<\/p>\n\n\n\n<p>However, if you&#8217;re writing a dynamic solution like a Markdown to HTML converter with a real-time preview, then <code>innerHTML<\/code> is the way to go as it is a &#8220;one-size-fits-all&#8221; approach. This is precisely what&#8217;s required for this particular conversion and real-time preview. Building the same logic with <code>createElement<\/code> would be a pain and render highly coupled logic with unextendable code.&nbsp;<\/p>\n\n\n\n<p>We can see this if we take the same to-do list example discussed above but use <code>createElement<\/code> instead of <code>innerHTML<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">const<\/span> todos = &#91;\n  <span class=\"hljs-string\">\"Exercise\"<\/span>,\n  <span class=\"hljs-string\">\"Have milk\"<\/span>,\n  <span class=\"hljs-string\">\"Buy bread\"<\/span>,\n  <span class=\"hljs-string\">\"Walk the dog\"<\/span>,\n  <span class=\"hljs-string\">\"Sleep timely :)\"<\/span>\n];\n<span class=\"hljs-keyword\">const<\/span> todoListPreview = <span class=\"hljs-built_in\">document<\/span>.getElementById(\u201ctdlst-preview\u201d); <span class=\"hljs-comment\">\/\/ returns an &lt;ol&gt; element<\/span>\ntodos.forEach(<span class=\"hljs-function\">(<span class=\"hljs-params\">todo<\/span>) =&gt;<\/span> {\n  todoListPreview.innerHTML += <span class=\"hljs-string\">`&lt;li&gt;<span class=\"hljs-subst\">${todo}<\/span>&lt;\/li&gt;`<\/span>;\n  <span class=\"hljs-keyword\">const<\/span> listItemElement = <span class=\"hljs-built_in\">document<\/span>.createElement(\u201cli\u201d);\n  listItemElement.textContent = todo;\n  todoListPreview.appendchild(listItemElement);\n\n});\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This will work for this particular problem, but it&#8217;s not a scalable approach.&nbsp;<\/p>\n\n\n\n<p>You&#8217;d be adding a lot of work for yourself and your team if, for example, you wanted to build a Rich Text Editor where you have to write separate logic for each of the new functionalities you might add.<\/p>\n\n\n\n<p>Using innerHTML with a simple HTML sanitizer is a much more efficient and practical approach.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Conclusion<\/strong><\/h2>\n\n\n\n<p>In HTML, assigning a string to the <code>innerHTML<\/code> property is okay in some situations. However, if you can&#8217;t be sure of what the string contains\u2014for example, if a user provides it and it is possibly malicious\u2014it&#8217;s best to use <code>createElement<\/code> or sanitize the input before storing it in a database.&nbsp;<\/p>\n\n\n\n<p>The open-source &#8220;sanitization&#8221; libraries strip off specific tags and attributes to make the desired HTML string XSS proof. Sanitization should be done on both the frontend and backend as a best practice to help reduce the risk of XSS attacks. When implementing sanitization on the frontend, it should be done at the time of render when the user provides input. And, on the backend, it should be done before storage in the database.&nbsp;<\/p>\n\n\n\n<p><em>This post was written by Keshav Malik. <\/em><a href=\"https:\/\/theinfosecguy.xyz\/\" target=\"_blank\" rel=\"noreferrer noopener\"><em>Keshav<\/em><\/a><em> is a full-time developer who loves to build and break stuff. He is constantly looking for new and exciting technologies and enjoys working with diverse technologies in his spare time. He loves music and plays badminton whenever the opportunity presents itself.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Walk through the pros &#038; cons of using the JavaScript innerHTML property and how to use it safely without exposing yourself  to XSS attacks.<\/p>\n","protected":false},"author":12,"featured_media":18989,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[9],"tags":[],"persona":[29],"blog-programming-language":[62],"keyword-cluster":[],"class_list":["post-17269","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development"],"acf":[],"_links":{"self":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/17269","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/users\/12"}],"replies":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/comments?post=17269"}],"version-history":[{"count":60,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/17269\/revisions"}],"predecessor-version":[{"id":18998,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/17269\/revisions\/18998"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/media\/18989"}],"wp:attachment":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/media?parent=17269"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/categories?post=17269"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/tags?post=17269"},{"taxonomy":"persona","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/persona?post=17269"},{"taxonomy":"blog-programming-language","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/blog-programming-language?post=17269"},{"taxonomy":"keyword-cluster","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/keyword-cluster?post=17269"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}