Element: innerHTML property
Baseline Widely available *
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
* Some parts of this feature may have varying levels of support.
Warning: This property parses its input as HTML, writing the result into the DOM. APIs like this are known as injection sinks, and are potentially a vector for cross-site-scripting (XSS) attacks, if the input originally came from an attacker.
You can reduce the risk by assigning TrustedHTML
objects instead of strings, and enforcing trusted types using the require-trusted-types-for
CSP directive.
This ensures that the input is passed through a transformation function, which has the chance to sanitize the input to remove potentially dangerous markup, such as <script>
elements and event handler attributes.
The innerHTML
property of the Element
interface gets or sets the HTML or XML markup contained within the element, omitting any shadow roots in both cases.
To insert the HTML into the document rather than replace the contents of an element, use the method insertAdjacentHTML()
.
Value
Getting the property returns a string containing the HTML serialization of the element's descendants.
Setting the value of innerHTML
removes all of the element's descendants and replaces them with nodes constructed by parsing the HTML given in the assigned TrustedHTML
or string.
When set to the null
value, that null
value is converted to the empty string (""
), so elt.innerHTML = null
is equivalent to elt.innerHTML = ""
.
Note that <script>
elements in the assigned value are injected but not executed.
Shadow roots are dropped from the serialized and injected HTML.
Exceptions
SyntaxError
DOMException
-
Thrown if an attempt was made to set the value of
innerHTML
using a string which is not properly-formed HTML. TypeError
-
Thrown if the property is set to a string when Trusted Types are enforced by a CSP and no default policy is defined.
NoModificationAllowedError
DOMException
-
Thrown if an attempt was made to insert the HTML into a node whose parent is a
Document
.
Description
innerHTML
gets a serialization of the nested child DOM elements within the element, or sets HTML or XML that should be parsed to replace the DOM tree within the element.
The serialization of the DOM tree read from the property does not include shadow roots — if you want to get a HTML string that includes shadow roots, you must instead use the Element.getHTML()
or ShadowRoot.getHTML()
methods.
Similarly, when setting element content using innerHTML
, the HTML string is parsed into DOM elements that do not contain shadow roots.
So for example <template>
is parsed into as HTMLTemplateElement
, whether or not the shadowrootmode
attribute is specified.
In order to set an element's contents from an HTML string that includes declarative shadow roots, you must instead use Element.setHTMLUnsafe()
or ShadowRoot.setHTMLUnsafe()
.
Note that some browsers serialize the <
and >
characters as <
and >
when they appear in attribute values (see Browser compatibility).
This is to prevent a potential security vulnerability (mutation XSS) in which an attacker can craft input that bypasses a sanitization function, enabling a cross-site scripting (XSS) attack.
Security considerations
The innerHTML
property is probably the most common vector for Cross-site-scripting (XSS) attacks, where potentially unsafe strings provided by a user are injected into the DOM without first being sanitized.
While the property does prevent <script>
elements from executing when they are injected, it is susceptible to many other ways that attackers can craft HTML to run malicious JavaScript.
For example, the following example would execute the code in the error
event handler, because the <img>
src
value is not a valid image URL:
const name = "<img src='x' onerror='alert(1)'>";
el.innerHTML = name; // shows the alert
You can mitigate these issues by always assigning TrustedHTML
objects instead of strings, and enforcing trusted type using the require-trusted-types-for
CSP directive.
This ensures that the input is passed through a transformation function, which has the chance to sanitize the input to remove potentially dangerous markup before it is injected.
This is demonstrated in the following sections.
Note:
Node.textContent
should be used when you know that the user provided content should be plain text.
This prevents it being parsed as HTML.
Examples
Reading the HTML contents of an element
Reading innerHTML
causes the user agent to serialize the HTML or XML fragment comprised of the element's descendants.
The resulting string is returned.
const contents = myElement.innerHTML;
This lets you look at the HTML markup of the element's content nodes.
Note: The returned HTML or XML fragment is generated based on the current contents of the element, so the markup and formatting of the returned fragment may not match the original page markup. It will also omit any shadow roots.
Replacing the contents of an element
Setting the value of innerHTML
lets you replace the existing contents of an element with a new DOM tree parsed from an input.
This completely removes the original content, including any event handlers associated with the removed elements.
Trusted types are not yet supported on all browsers, so first we define the trusted types tinyfill.
This provides an implementation of trustedTypes.createPolicy()
which just returns the policyOptions
object it was passed.
The object defines sanitization functions for data, and these functions are expected to return strings.
Effectively it is a transparent replacement for the trusted types JavaScript API:
if (typeof trustedTypes === "undefined")
trustedTypes = { createPolicy: (n, rules) => rules };
Next create a TrustedTypePolicy
that defines a createHTML()
for transforming an input string into TrustedHTML
instances.
Commonly implementations of createHTML()
use a library such as DOMPurify to sanitize the input as shown below:
const policy = trustedTypes.createPolicy("my-policy", {
createHTML: (input) => DOMPurify.sanitize(input),
});
Then use this policy
object to create a TrustedHTML
object from the potentially unsafe input string, and assign the result to the element:
// The potentially malicious string
const untrustedString = "<p>I might be XSS</p><img src='x' onerror='alert(1)'>";
// Create a TrustedHTML instance using the policy
const trustedHTML = policy.createHTML(untrustedString);
// Inject the TrustedHTML (which contains a trusted string)
const element = document.querySelector("#container");
element.innerHTML = trustedHTML;
Warning:
While you can directly assign a string to innerHTML
this is a security risk if the string to be inserted might contain potentially malicious content.
You should use TrustedHTML
to ensure that the content is sanitized before it is inserted, and you should set a CSP header to enforce trusted types.
Clearing the contents of an element
You can clear the content of an element by setting its content to the empty string.
For example, the code below clears the entire contents of a document by setting the body
element to a TrustedHTML
object for the empty string:
// Create a TrustedHTML instance using the policy
document.body.textContent = policy.createHTML(policy.createHTML(""));
Note that in this case we're setting the element to an empty string, which we know is safe. If we weren't enforcing trusted types we could instead directly assign the empty string:
document.body.textContent = "";
Logging messages using innerHTML
This example uses innerHTML
to create a mechanism for logging messages into a box on a web page.
HTML
The HTML is quite simple for our example.
The <div>
with the class "box"
is just a container for layout purposes, presenting the contents with a box around it.
The <div>
whose class is "log"
is the container for the log text itself.
CSS
The following CSS styles our example content.
JavaScript
The example doesn't actually require sanitization/trusted types because we know that the strings will only contain <strong>
and <em>
elements.
However we'll use them anyway, because in a real application you should enforce and use trusted types everywhere.
First we define the tinyfill:
Then we create our policy "safe-string-policy"
for using with inputs that we know to be safe.
This just passes the input through to the output without performing sanitization:
Next we define a log()
function that uses this policy.
The log()
function creates the log output by getting the current time from a Date
object using toLocaleTimeString()
, and building a string with the timestamp and the message text.
This string is appended to the original content of the log and then the policy is used to pass the string through our createHTML()
"transformation" function.
Then the message is assigned to the element with the class "log"
.
We add a second method that logs information about MouseEvent
based events (such as mousedown
, click
, and mouseenter
):
Then we use this as the event handler for a number of mouse events on the box that contains our log:
Result
The resulting content looks like this. You can see output into the log by moving the mouse in and out of the box, clicking in it, and so forth.
Specifications
Specification |
---|
HTML # dom-element-innerhtml |
Browser compatibility
See also
Node.textContent
andHTMLElement.innerText
Element.insertAdjacentHTML()
Element.outerHTML
- Parsing HTML or XML into a DOM tree:
DOMParser
- Serializing a DOM tree into an XML string:
XMLSerializer
Element.getHTML()
ShadowRoot.getHTML()
Element.setHTMLUnsafe()
ShadowRoot.setHTMLUnsafe()
- Trusted Types API