Browser Support Note:

  • setHTMLUnsafe is supported across all browsers.
  • setHTML is in the process of being standardized and is currently available only in Firefox behind a flag.
  • getHTML has been supported in Chrome and Edge since version 125.

Introduction to setHTMLUnsafe

Recently, browsers have introduced a new method called setHTMLUnsafe. The term “unsafe” in this context means that, similar to innerHTML, it does not sanitize the input. This naming is somewhat inconsistent with previous APIs such as innerHTML and eval(), which do not have the “Unsafe” suffix despite similar risks. The key difference now is that we have both a safe version (setHTML) and an unsafe version (setHTMLUnsafe).

According to the Sanitizer API spec:

The “safe” methods will not generate any markup that executes scripts, making them safe from XSS (Cross-Site Scripting) attacks.

Consider this example:

form.addEventListener('submit', function(event) {
    event.preventDefault();
    const markup = `<h2>${input.value}</h2>`;
    div.innerHTML = markup;
});

If a user inputs <img src=doesnotexist onerror="alert('Potential XSS Attack')">, the JavaScript code will execute in the browser. This risk persists with setHTMLUnsafe.

However, using setHTML, only the image tag <img src="doesnotexist"> is inserted, stripping out any JavaScript. This enhances security by preventing the execution of potentially malicious scripts.

The Role of setHTMLUnsafe

With the advent of setHTML, one might wonder why we need setHTMLUnsafe. The answer lies in the implementation of declarative shadow DOM.

The <template> element in HTML serves two purposes:

  1. Holding a fragment of HTML that can be used later via JavaScript.
  2. Creating a shadow DOM instantly when the shadowrootmode attribute is present.

While innerHTML works well for the first use case, it fails to handle the second. For example:

const main = document.querySelector('main');
main.innerHTML = `
    <h2>I am in the Light DOM</h2>
    <div>
        <template shadowrootmode="open">
            <style>
                h2 { color: blue; }
            </style>
            <h2>Shadow DOM</h2>
        </template>
    </div>`;

In this case, innerHTML injects the <template> element but does not render its contents as shadow DOM, regardless of the shadowrootmode attribute.

Conversely, using setHTML:

const main = document.querySelector('main');
main.setHTML(`
    <h2>I am in the Light DOM</h2>
    <div>
        <template shadowrootmode="open">
            <style>
                h2 { color: blue; }
            </style>
            <h2>Shadow DOM</h2>
        </template>
    </div>`);

Here, the <template> and its contents are removed, treating it as an “unsafe node.” To dynamically add declarative shadow DOM, browsers introduced setHTMLUnsafe:

const main = document.querySelector('main');
main.setHTMLUnsafe(`
    <h2>I am in the Light DOM</h2>
    <div>
        <template shadowrootmode="open">
            <style>
                h2 { color: blue; }
            </style>
            <h2>Shadow DOM</h2>
        </template>
    </div>`);

Using setHTMLUnsafe, the contents of the <template> are rendered inside the shadow DOM.

Read more @mozilla.org

Read also: 20 Must Know Laptop Shortcuts For Developers And Everyone

Introducing getHTML

setHTML and setHTMLUnsafe do not fully replace innerHTML, which can both set and get HTML. The complementary function for getting HTML content is getHTML.

const main = document.querySelector('main');
const html = main.getHTML();

By default, getHTML does not return any markup from within a shadow DOM, but it can be configured to do so:

const main = document.querySelector('main');
const html = main.getHTML({ serializableShadowRoots: true });

Setting serializableShadowRoots to true will serialize all shadow DOM trees that have opted in for serialization. A template element can opt-in using the shadowrootserializable attribute:

<template shadowrootmode="open" shadowrootserializable></template>

Similarly, in JavaScript, the attachShadow method has a boolean serializable option:

this.attachShadow({ mode: "open", serializable: true });

It is also possible to serialize only specific shadow DOM trees by passing an array of shadow roots:

const markup = main.getHTML({
    shadowRoots: [document.querySelector('.example').shadowRoot]
});

All shadow roots in the array will be serialized, even if they are not marked as serializable.

Read more @mozilla.org

These new methods—setHTMLUnsafe, setHTML, and getHTML—offer more control and flexibility for developers working with dynamic content and shadow DOMs in modern web development.

Conclusion

Understanding and leveraging these new methods can significantly enhance how you manage dynamic content on your web pages. Whether you need the safety of setHTML or the flexibility of setHTMLUnsafe for advanced use cases, these tools provide robust solutions for modern web development.

Source: BlogTech