Using custom elements - Web APIs

Using custom elements
=====================

One of the key features of web components is the ability to create *custom elements*: that is, HTML elements whose behavior is defined by the web developer, that extend the set of elements available in the browser.

This article introduces custom elements, and walks through some examples.

Types of custom element
-----------------------

There are two types of custom element:

* **Customized built-in elements** inherit from standard HTML elements such as `HTMLImageElement` or `HTMLParagraphElement`. Their implementation customizes the behavior of the standard element.
* **Autonomous custom elements** inherit from the HTML element base class `HTMLElement`. You have to implement their behavior from scratch.
Implementing a custom element
-----------------------------

A custom element is implemented as a class which extends `HTMLElement` (in the case of autonomous elements) or the interface you want to customize (in the case of customized built-in elements).

Here's the implementation of a minimal custom element that customizes the `<p>` element:

```js
class WordCount extends HTMLParagraphElement {
  constructor() {
    super();
  }
  // Element functionality written in here
}
```

Here's the implementation of a minimal autonomous custom element:

```js
class PopupInfo extends HTMLElement {
  constructor() {
    super();
  }
  // Element functionality written in here
}
```

In the class constructor, you can set up initial state and default values, register event listeners and perhaps create a shadow root. At this point, you should not inspect the element's attributes or children, or add new attributes or children. See Requirements for custom element constructors and reactions for the complete set of requirements.

### Custom element lifecycle callbacks

Once your custom element is registered, the browser will call certain methods of your class when code in the page interacts with your custom element in certain ways. By providing an implementation of these methods, which the specification calls *lifecycle callbacks*, you can run code in response to these events.

Custom element lifecycle callbacks include:

* `connectedCallback()`: called each time the element is added to the document. The specification recommends that, as far as possible, developers should implement custom element setup in this callback rather than the constructor.
* `disconnectedCallback()`: called each time the element is removed from the document.
* `adoptedCallback()`: called each time the element is moved to a new document.
* `attributeChangedCallback()`: called when attributes are changed, added, removed, or replaced. See Responding to attribute changes for more details about this callback.

Here's a minimal custom element that logs these lifecycle events:

```js
// Create a class for the element
class MyCustomElement extends HTMLElement {
  static observedAttributes = ["color", "size"];

  constructor() {
    // Always call super first in constructor
    super();
  }

  connectedCallback() {
    console.log("Custom element added to page.");
  }

  disconnectedCallback() {
    console.log("Custom element removed from page.");
  }

  adoptedCallback() {
    console.log("Custom element moved to new page.");
  }

  attributeChangedCallback(name, oldValue, newValue) {
    console.log(`Attribute ${name} has changed.`);
  }
}

customElements.define("my-custom-element", MyCustomElement);
```

Registering a custom element
----------------------------

To make a custom element available in a page, call the `define()` method of `Window.customElements`.

The `define()` method takes the following arguments:

`name`

The name of the element. This must start with a lowercase letter, contain a hyphen, and satisfy certain other rules listed in the specification's definition of a valid name.

`constructor`

The custom element's constructor function.

`options`

Only included for customized built-in elements, this is an object containing a single property `extends`, which is a string naming the built-in element to extend.

For example, this code registers the `WordCount` customized built-in element:

```js
customElements.define("word-count", WordCount, { extends: "p" });
```

This code registers the `PopupInfo` autonomous custom element:

```js
customElements.define("popup-info", PopupInfo);
```

Using a custom element
----------------------

Once you've defined and registered a custom element, you can use it in your code.

To use a customized built-in element, use the built-in element but with the custom name as the value of the `is` attribute:

```html
<p is="word-count"></p>
```

To use an autonomous custom element, use the custom name just like a built-in HTML element:

```html
<popup-info>
  <!-- content of the element -->
</popup-info>
```

Responding to attribute changes
-------------------------------

Like built-in elements, custom elements can use HTML attributes to configure the element's behavior. To use attributes effectively, an element has to be able to respond to changes in an attribute's value. To do this, a custom element needs to add the following members to the class that implements the custom element:

* A static property named `observedAttributes`. This must be an array containing the names of all attributes for which the element needs change notifications.
* An implementation of the `attributeChangedCallback()` lifecycle callback.

The `attributeChangedCallback()` callback is then called whenever an attribute whose name is listed in the element's `observedAttributes` property is added, modified, removed, or replaced.

The callback is passed three arguments:

* The name of the attribute which changed.
* The attribute's old value.
* The attribute's new value.

For example, this autonomous element will observe a `size` attribute, and log the old and new values when they change:

```js
// Create a class for the element
class MyCustomElement extends HTMLElement {
  static observedAttributes = ["size"];

  constructor() {
    super();
  }

  attributeChangedCallback(name, oldValue, newValue) {
    console.log(
      `Attribute ${name} has changed from ${oldValue} to ${newValue}.`,
    );
  }
}

customElements.define("my-custom-element", MyCustomElement);
```

Note that if the element's HTML declaration includes an observed attribute, then `attributeChangedCallback()` will be called after the attribute is initialized, when the element's declaration is parsed for the first time. So in the following example, `attributeChangedCallback()` will be called when the DOM is parsed, even if the attribute is never changed again:

```html
<my-custom-element size="100"></my-custom-element>
```

For a complete example showing the use of `attributeChangedCallback()`, see Lifecycle callbacks in this page.

### Custom states and custom state pseudo-class CSS selectors

 Built in HTML elements can have different *states*, such as "hover", "disabled", and "read only".
 Some of these states can be set as attributes using HTML or JavaScript, while others are internal, and cannot.
 Whether external or internal, commonly these states have corresponding CSS pseudo-classes that can be used to select and style the element when it is in a particular state.

 Autonomous custom elements (but not elements based on built-in elements) also allow you to define states and select against them using *custom state pseudo-classes*.
 The code below shows how this works using the example of an autonomous custom element that has an internal state "`collapsed`".

 The `collapsed` state is represented as a boolean property (with setter and getter methods) that is not visible outside of the element.
 To make this state selectable in CSS the custom element first calls `HTMLElement.attachInternals()` in its constructor in order to attach an `ElementInternals` object, which in turn provides access to a `CustomStateSet` through the `ElementInternals.states` property.
 The setter for the (internal) collapsed state adds the *dashed identifier* `--hidden` to the `CustomStateSet` when the state is `true`, and removes it when the state is `false`.
 The dashed identifier is just a string preceded by two dashes: in this case we called it `--hidden`, but we could have just as easily called it `--collapsed`.

```js
class MyCustomElement extends HTMLElement {
  constructor() {
    super();
    this._internals = this.attachInternals();
  }

  get collapsed() {
    return this._internals.states.has("--hidden");
  }

  set collapsed(flag) {
    if (flag) {
      // Existence of identifier corresponds to "true"
      this._internals.states.add("--hidden");
    } else {
      // Absence of identifier corresponds to "false"
      this._internals.states.delete("--hidden");
    }
  }
}

// Register the custom element
customElements.define("my-custom-element", MyCustomElement);
```

 After adding `<my-custom-element>` to the HTML we can use the dashed identifier added to the `CustomStateSet`, prefixed with `:`, as a custom state pseudo-class for selecting the element state.
 For example, below we select on the `--hidden` state being true (and hence the element's `collapsed` state) using the `:--hidden` selector, and remove the border.

```css
my-custom-element {
  border: dashed red;
}
my-custom-element:--hidden {
  border: none;
}
```

There is are more complete live example in `CustomStateSet`.

Examples
--------

In the rest of this guide we'll look at a few example custom elements. You can find the source for all these examples, and more, in the web-components-examples repository, and you can see them all live at https://mdn.github.io/web-components-examples/.

### An autonomous custom element

First, we'll look at an autonomous custom element. The `<popup-info>` custom element takes an image icon and a text string as attributes, and embeds the icon into the page. When the icon is focused, it displays the text in a pop up information box to provide further in-context information.

* See the example running live
* See the source code

To begin with, the JavaScript file defines a class called `PopupInfo`, which extends the `HTMLElement` class.

```js
// Create a class for the element
class PopupInfo extends HTMLElement {
  constructor() {
    // Always call super first in constructor
    super();
  }

  connectedCallback() {
    // Create a shadow root
    const shadow = this.attachShadow({ mode: "open" });

    // Create spans
    const wrapper = document.createElement("span");
    wrapper.setAttribute("class", "wrapper");

    const icon = document.createElement("span");
    icon.setAttribute("class", "icon");
    icon.setAttribute("tabindex", 0);

    const info = document.createElement("span");
    info.setAttribute("class", "info");

    // Take attribute content and put it inside the info span
    const text = this.getAttribute("data-text");
    info.textContent = text;

    // Insert icon
    let imgUrl;
    if (this.hasAttribute("img")) {
      imgUrl = this.getAttribute("img");
    } else {
      imgUrl = "img/default.png";
    }

    const img = document.createElement("img");
    img.src = imgUrl;
    icon.appendChild(img);

    // Create some CSS to apply to the shadow dom
    const style = document.createElement("style");
    console.log(style.isConnected);

    style.textContent = `
 .wrapper {
 position: relative;
 }

 .info {
 font-size: 0.8rem;
 width: 200px;
 display: inline-block;
 border: 1px solid black;
 padding: 10px;
 background: white;
 border-radius: 10px;
 opacity: 0;
 transition: 0.6s all;
 position: absolute;
 bottom: 20px;
 left: 10px;
 z-index: 3;
 }

 img {
 width: 1.2rem;
 }

 .icon:hover + .info, .icon:focus + .info {
 opacity: 1;
 }
 `;

    // Attach the created elements to the shadow dom
    shadow.appendChild(style);
    console.log(style.isConnected);
    shadow.appendChild(wrapper);
    wrapper.appendChild(icon);
    wrapper.appendChild(info);
  }
}
```

The class definition contains the `constructor()` for the class, which always starts by calling `super()` so that the correct prototype chain is established.

Inside the method `connectedCallback()`, we define all the functionality the element will have when the element is connected to the DOM. In this case we attach a shadow root to the custom element, use some DOM manipulation to create the element's internal shadow DOM structure — which is then attached to the shadow root — and finally attach some CSS to the shadow root to style it. We don't do this work in the constructor because an element's attributes are unavailable until it is connected to the DOM.

Finally, we register our custom element in the `CustomElementRegistry` using the `define()` method we mentioned earlier — in the parameters we specify the element name, and then the class name that defines its functionality:

```js
customElements.define("popup-info", PopupInfo);
```

It is now available to use on our page. Over in our HTML, we use it like so:

```html
<popup-info
 img="img/alt.png"
 data-text="Your card validation code (CVC)
 is an extra security feature — it is the last 3 or 4 numbers on the
 back of your card."></popup-info>
```

### Referencing external styles

In the above example we apply styles to the shadow DOM using a `<style>` element, but you can reference an external stylesheet from a `<link>` element instead. In this example we'll modify the `<popup-info>` custom element to use an external stylesheet.

* See the example running live
* See the source code

Here's the class definition:

```js
// Create a class for the element
class PopupInfo extends HTMLElement {
  constructor() {
    // Always call super first in constructor
    super();
  }

  connectedCallback() {
    // Create a shadow root
    const shadow = this.attachShadow({ mode: "open" });

    // Create spans
    const wrapper = document.createElement("span");
    wrapper.setAttribute("class", "wrapper");

    const icon = document.createElement("span");
    icon.setAttribute("class", "icon");
    icon.setAttribute("tabindex", 0);

    const info = document.createElement("span");
    info.setAttribute("class", "info");

    // Take attribute content and put it inside the info span
    const text = this.getAttribute("data-text");
    info.textContent = text;

    // Insert icon
    let imgUrl;
    if (this.hasAttribute("img")) {
      imgUrl = this.getAttribute("img");
    } else {
      imgUrl = "img/default.png";
    }

    const img = document.createElement("img");
    img.src = imgUrl;
    icon.appendChild(img);

    // Apply external styles to the shadow dom
    const linkElem = document.createElement("link");
    linkElem.setAttribute("rel", "stylesheet");
    linkElem.setAttribute("href", "style.css");

    // Attach the created elements to the shadow dom
    shadow.appendChild(linkElem);
    shadow.appendChild(wrapper);
    wrapper.appendChild(icon);
    wrapper.appendChild(info);
  }
}
```

It's just like the original `<popup-info>` example, except that we link to an external stylesheet using a `<link>` element, which we add to the shadow DOM.

Note that `<link>` elements do not block paint of the shadow root, so there may be a flash of unstyled content (FOUC) while the stylesheet loads.

Many modern browsers implement an optimization for `<style>` tags either cloned from a common node or that have identical text, to allow them to share a single backing stylesheet. With this optimization the performance of external and internal styles should be similar.

### Customized built-in elements

Now let's have a look at a customized built in element example. This example extends the built-in `<ul>` element to support expanding and collapsing the list items.

* See the example running live
* See the source code

First of all, we define our element's class:

```js
// Create a class for the element
class ExpandingList extends HTMLUListElement {
  constructor() {
    // Always call super first in constructor
    // Return value from super() is a reference to this element
    self = super();
  }

  connectedCallback() {
    // Get ul and li elements that are a child of this custom ul element
    // li elements can be containers if they have uls within them
    const uls = Array.from(self.querySelectorAll("ul"));
    const lis = Array.from(self.querySelectorAll("li"));
    // Hide all child uls
    // These lists will be shown when the user clicks a higher level container
    uls.forEach((ul) => {
      ul.style.display = "none";
    });

    // Look through each li element in the ul
    lis.forEach((li) => {
      // If this li has a ul as a child, decorate it and add a click handler
      if (li.querySelectorAll("ul").length > 0) {
        // Add an attribute which can be used by the style
        // to show an open or closed icon
        li.setAttribute("class", "closed");

        // Wrap the li element's text in a new span element
        // so we can assign style and event handlers to the span
        const childText = li.childNodes[0];
        const newSpan = document.createElement("span");

        // Copy text from li to span, set cursor style
        newSpan.textContent = childText.textContent;
        newSpan.style.cursor = "pointer";

        // Add click handler to this span
        newSpan.addEventListener("click", (e) => {
          // next sibling to the span should be the ul
          const nextul = e.target.nextElementSibling;

          // Toggle visible state and update class attribute on ul
          if (nextul.style.display == "block") {
            nextul.style.display = "none";
            nextul.parentNode.setAttribute("class", "closed");
          } else {
            nextul.style.display = "block";
            nextul.parentNode.setAttribute("class", "open");
          }
        });
        // Add the span and remove the bare text node from the li
        childText.parentNode.insertBefore(newSpan, childText);
        childText.parentNode.removeChild(childText);
      }
    });
  }
}
```

Note that this time we extend `HTMLUListElement`, rather than `HTMLElement`. This means that we get the default behavior of a list, and only have to implement our own customizations.

As before, most of the code is in the `connectedCallback()` lifecycle callback.

Next, we register the element using the `define()` method as before, except that this time it also includes an options object that details what element our custom element inherits from:

```js
customElements.define("expanding-list", ExpandingList, { extends: "ul" });
```

Using the built-in element in a web document also looks somewhat different:

```html
<ul is="expanding-list">
  …
</ul>
```

You use a `<ul>` element as normal, but specify the name of the custom element inside the `is` attribute.

Note that in this case we must ensure that the script defining our custom element is executed after the DOM has been fully parsed, because `connectedCallback()` is called as soon as the expanding list is added to the DOM, and at that point its children have not been added yet, so the `querySelectorAll()` calls will not find any items. One way to ensure this is to add the defer attribute to the line that includes the script:

```html
<script src="main.js" defer></script>
```

### Lifecycle callbacks

So far we've seen only one lifecycle callback in action: `connectedCallback()`. In the final example, `<custom-square>`, we'll see some of the others. The `<custom-square>` autonomous custom element draws a square whose size and color are determined by two attributes, named `"size"` and `"color"`.

* See the example running live
* See the source code

In the class constructor, we attach a shadow DOM to the element, then attach empty `<div>` and `<style>` elements to the shadow root:

```js
constructor() {
  // Always call super first in constructor
  super();

  const shadow = this.attachShadow({ mode: "open" });

  const div = document.createElement("div");
  const style = document.createElement("style");
  shadow.appendChild(style);
  shadow.appendChild(div);
}
```

The key function in this example is `updateStyle()` — this takes an element, gets its shadow root, finds its `<style>` element, and adds `width`, `height`, and `background-color` to the style.

```js
function updateStyle(elem) {
  const shadow = elem.shadowRoot;
  shadow.querySelector("style").textContent = `
 div {
 width: ${elem.getAttribute("size")}px;
 height: ${elem.getAttribute("size")}px;
 background-color: ${elem.getAttribute("color")};
 }
 `;
}
```

The actual updates are all handled by the lifecycle callbacks. The `connectedCallback()` runs each time the element is added to the DOM — here we run the `updateStyle()` function to make sure the square is styled as defined in its attributes:

```js
connectedCallback() {
  console.log("Custom square element added to page.");
  updateStyle(this);
}
```

The `disconnectedCallback()` and `adoptedCallback()` callbacks log messages to the console to inform us when the element is either removed from the DOM, or moved to a different page:

```js
disconnectedCallback() {
  console.log("Custom square element removed from page.");
}

adoptedCallback() {
  console.log("Custom square element moved to new page.");
}
```

The `attributeChangedCallback()` callback is run whenever one of the element's attributes is changed in some way. As you can see from its parameters, it is possible to act on attributes individually, looking at their name, and old and new attribute values. In this case however, we are just running the `updateStyle()` function again to make sure that the square's style is updated as per the new values:

```js
attributeChangedCallback(name, oldValue, newValue) {
  console.log("Custom square element attributes changed.");
  updateStyle(this);
}
```

Note that to get the `attributeChangedCallback()` callback to fire when an attribute changes, you have to observe the attributes. This is done by specifying a `static get observedAttributes()` method inside the custom element class - this should return an array containing the names of the attributes you want to observe:

```js
static get observedAttributes() {
  return ["color", "size"];
}
```


Using templates and slots - Web APIs

Using templates and slots
=========================

This article explains how you can use the `<template>` and `<slot>` elements to create a flexible template that can then be used to populate the shadow DOM of a web component.

The truth about templates
-------------------------

 When you have to reuse the same markup structures repeatedly on a web page, it makes sense to use some kind of a template rather than repeating the same structure over and over again.
 This was possible before, but it is made a lot easier by the HTML `<template>` element.
 This element and its contents are not rendered in the DOM, but it can still be referenced using JavaScript.

Let's look at a trivial quick example:

```html
<template id="my-paragraph">
  <p>My paragraph</p>
</template>
```

This won't appear in your page until you grab a reference to it with JavaScript and then append it to the DOM, using something like the following:

```js
let template = document.getElementById("my-paragraph");
let templateContent = template.content;
document.body.appendChild(templateContent);
```

Although trivial, you can already start to see how this could be useful.

Using templates with web components
-----------------------------------

 Templates are useful on their own, but they work even better with web components.
 Let's define a web component that uses our template as the content of its shadow DOM.
 We'll call it `<my-paragraph>` as well:

```js
customElements.define(
  "my-paragraph",
  class extends HTMLElement {
    constructor() {
      super();
      let template = document.getElementById("my-paragraph");
      let templateContent = template.content;

      const shadowRoot = this.attachShadow({ mode: "open" });
      shadowRoot.appendChild(templateContent.cloneNode(true));
    }
  },
);
```

The key point to note here is that we append a clone of the template content to the shadow root, created using the `Node.cloneNode()` method.

 And because we are appending its contents to a shadow DOM, we can include some styling information inside the template in a `<style>` element, which is then encapsulated inside the custom element.
 This wouldn't work if we just appended it to the standard DOM.

So for example:

```html
<template id="my-paragraph">
  <style>
 p {
 color: white;
 background-color: #666;
 padding: 5px;
 }
 </style>
  <p>My paragraph</p>
</template>
```

Now we can use it by just adding it to our HTML document:

```html
<my-paragraph></my-paragraph>
```

Adding flexibility with slots
-----------------------------

 So far so good, but the element isn't very flexible.
 We can only display one bit of text inside it, meaning that at the moment it is even less useful than a regular paragraph! We can make it possible to display different text in each element instance in a nice declarative way using the `<slot>` element.

Slots are identified by their `name` attribute, and allow you to define placeholders in your template that can be filled with any markup fragment you want when the element is used in the markup.

So, if we want to add a slot into our trivial example, we could update our template's paragraph element like this:

```html
<p><slot name="my-text">My default text</slot></p>
```

If the slot's content isn't defined when the element is included in the markup, or if the browser doesn't support slots, `<my-paragraph>` just contains the fallback content "My default text".

To define the slot's content, we include an HTML structure inside the `<my-paragraph>` element with a `slot` attribute whose value is equal to the name of the slot we want it to fill. As before, this can be anything you like, for example:

```html
<my-paragraph>
  <span slot="my-text">Let's have some different text!</span>
</my-paragraph>
```

or

```html
<my-paragraph>
  <ul slot="my-text">
    <li>Let's have some different text!</li>
    <li>In a list!</li>
  </ul>
</my-paragraph>
```

**Note:** Nodes that can be inserted into slots are known as *Slottable* nodes; when a node has been inserted in a slot, it is said to be *slotted*.

**Note:** An unnamed `<slot>` will be filled with all of the custom element's top-level child nodes that do not have the `slot` attribute. This includes text nodes.

 And that's it for our trivial example.
 If you want to play with it some more, you can find it on GitHub (see it running live also).

A more involved example
-----------------------

To finish off the article, let's look at something a little less trivial.

The following set of code snippets show how to use `<slot>` together with `<template>` and some JavaScript to:

* create a **`<element-details>`** element with named slots in its shadow root
* design the **`<element-details>`** element in such a way that, when used in documents, it is rendered from composing the element's content together with content from its shadow root—that is, pieces of the element's content are used to fill in named slots in its shadow root

 Note that it is technically possible to use `<slot>` element without a `<template>` element, e.g., within say a regular `<div>` element, and still take advantage of the place-holder features of `<slot>` for Shadow DOM content, and doing so may indeed avoid the small trouble of needing to first access the template element's `content` property (and clone it).
 However, it is generally more practical to add slots within a `<template>` element, since you are unlikely to need to define a pattern based on an already-rendered element.

In addition, even if it is not already rendered, the purpose of the container as a template should be more semantically clear when using the `<template>`. In addition, `<template>` can have items directly added to it, like `<td>`, which would disappear when added to a `<div>`.

**Note:** You can find this complete example at element-details (see it running live also).

### Creating a template with some slots

First of all, we use the `<slot>` element within a `<template>` element to create a new "element-details-template" document fragment containing some named slots:

```html
<template id="element-details-template">
  <style>
 details {
 font-family: "Open Sans Light", Helvetica, Arial;
 }
 .name {
 font-weight: bold;
 color: #217ac0;
 font-size: 120%;
 }
 h4 {
 margin: 10px 0 -8px 0;
 }
 h4 span {
 background: #217ac0;
 padding: 2px 6px 2px 6px;
 }
 h4 span {
 border: 1px solid #cee9f9;
 border-radius: 4px;
 }
 h4 span {
 color: white;
 }
 .attributes {
 margin-left: 22px;
 font-size: 90%;
 }
 .attributes p {
 margin-left: 16px;
 font-style: italic;
 }
 </style>
  <details>
    <summary>
      <span>
        <code class="name"
 ><<slot name="element-name">NEED NAME</slot>></code
 >
        <span class="desc"
 ><slot name="description">NEED DESCRIPTION</slot></span
 >
      </span>
    </summary>
    <div class="attributes">
      <h4><span>Attributes</span></h4>
      <slot name="attributes"><p>None</p></slot>
    </div>
  </details>
  <hr />
</template>
```

That `<template>` element has several features:

* The `<template>` has a `<style>` element with a set of CSS styles that are scoped just to the document fragment the `<template>` creates.
* The `<template>` uses `<slot>` and its `name` attribute to make three named slots:
    + `<slot name="element-name">`
    + `<slot name="description">`
    + `<slot name="attributes">`
* The `<template>` wraps the named slots in a `<details>` element.
### Creating a new <element-details> element from the <template>

 Next, let's create a new custom element named **`<element-details>`** and use `Element.attachShadow` to attach to it, as its shadow root, that document fragment we created with our `<template>` element above.
 This uses exactly the same pattern as we saw in our earlier trivial example.

```js
customElements.define(
  "element-details",
  class extends HTMLElement {
    constructor() {
      super();
      const template = document.getElementById(
        "element-details-template",
      ).content;
      const shadowRoot = this.attachShadow({ mode: "open" });
      shadowRoot.appendChild(template.cloneNode(true));
    }
  },
);
```

### Using the <element-details> custom element with named slots

Now let's take that **`<element-details>`** element and actually use it in our document:

```html
<element-details>
  <span slot="element-name">slot</span>
  <span slot="description"
 >A placeholder inside a web component that users can fill with their own
    markup, with the effect of composing different DOM trees together.</span
 >
  <dl slot="attributes">
    <dt>name</dt>
    <dd>The name of the slot.</dd>
  </dl>
</element-details>

<element-details>
  <span slot="element-name">template</span>
  <span slot="description"
 >A mechanism for holding client- side content that is not to be rendered
    when a page is loaded but may subsequently be instantiated during runtime
    using JavaScript.</span
 >
</element-details>
```

About that snippet, notice these points:

* The snippet has two instances of **`<element-details>`** elements which both use the `slot` attribute to reference the named slots `"element-name"` and `"description"` we put in the `<element-details>` shadow root .
* Only the first of those two **`<element-details>`** elements references the `"attributes"` named slot. The second `<element-details>` element lacks any reference to the `"attributes"` named slot.
* The first `<element-details>` element references the `"attributes"` named slot using a `<dl>` element with `<dt>` and `<dd>` children.
### Adding a final bit of style

As a finishing touch, we'll add a tiny bit more CSS for the `<dl>`, `<dt>`, and `<dd>` elements in our doc:

```css
dl {
  margin-left: 6px;
}
dt {
  color: #217ac0;
  font-family: Consolas, "Liberation Mono", Courier;
  font-size: 110%;
  font-weight: bold;
}
dd {
  margin-left: 16px;
}
```

```
body {
  margin-top: 47px;
}
```

### Result

Finally let's put all the snippets together and see what the rendered result looks like.

Notice the following points about this rendered result:

* Even though the instances of the **`<element-details>`** element in the document do not directly use the `<details>` element, they get rendered using `<details>` because the shadow root causes them to get populated with that.
* Within the rendered `<details>` output, the content in the **`<element-details>`** elements fills the named slots from the shadow root. In other words, the DOM tree from the **`<element-details>`** elements get *composed* together with the content of the shadow root.
* For both **`<element-details>`** elements, an **Attributes** heading gets automatically added from the shadow root before the position of the `"attributes"` named slot.
* Because the first **`<element-details>`** has a `<dl>` element which explicitly references the `"attributes"` named slot from its shadow root, the contents of that `<dl>` replace the `"attributes"` named slot from the shadow root.
* Because the second **`<element-details>`** doesn't explicitly reference the `"attributes"` named slot from its shadow root, its content for that named slot gets filled with the default content for it from the shadow root.


Using shadow DOM - Web APIs

Using shadow DOM
================

An important aspect of custom elements is encapsulation, because a custom element, by definition, is a piece of reusable functionality: it might be dropped into any web page and be expected to work. So it's important that code running in the page should not be able to accidentally break a custom element by modifying its internal implementation. Shadow DOM enables you to attach a DOM tree to an element, and have the internals of this tree hidden from JavaScript and CSS running in the page.

This article covers the basics of using the shadow DOM.

High-level view
---------------

This article assumes you are already familiar with the concept of the DOM (Document Object Model) — a tree-like structure of connected nodes that represents the different elements and strings of text appearing in a markup document (usually an HTML document in the case of web documents). As an example, consider the following HTML fragment:

```html
<html lang="en-US">
  <head>
    <meta charset="utf-8" />
    <title>DOM example</title>
  </head>
  <body>
    <section>
      <img src="dinosaur.png" alt="A red Tyrannosaurus Rex." />
      <p>
        Here we will add a link to the
        <a href="https://www.mozilla.org/">Mozilla homepage</a>
      </p>
    </section>
  </body>
</html>
```

This fragment produces the following DOM structure (excluding whitespace-only text nodes):
```

- HTML
    - HEAD
        - META charset="utf-8"
        - TITLE
            - #text: DOM example
    - BODY
        - SECTION
            - IMG src="dinosaur.png" alt="A red Tyrannosaurus Rex."
            - P
                - #text: Here we will add a link to the
                - A href="https://www.mozilla.org/"
                    - #text: Mozilla homepage
```

*Shadow* DOM allows hidden DOM trees to be attached to elements in the regular DOM tree — this shadow DOM tree starts with a shadow root, underneath which you can attach any element, in the same way as the normal DOM.

![SVG version of the diagram showing the interaction of document, shadow root and shadow host.](/en-US/docs/Web/API/Web_components/Using_shadow_DOM/shadowdom.svg)

There are some bits of shadow DOM terminology to be aware of:

* **Shadow host**: The regular DOM node that the shadow DOM is attached to.
* **Shadow tree**: The DOM tree inside the shadow DOM.
* **Shadow boundary**: the place where the shadow DOM ends, and the regular DOM begins.
* **Shadow root**: The root node of the shadow tree.

You can affect the nodes in the shadow DOM in exactly the same way as non-shadow nodes — for example appending children or setting attributes, styling individual nodes using element.style.foo, or adding style to the entire shadow DOM tree inside a `<style>` element. The difference is that none of the code inside a shadow DOM can affect anything outside it, allowing for handy encapsulation.

Before shadow DOM was made available to web developers, browsers were already using it to encapsulate the inner structure of an element. Think for example of a `<video>` element, with the default browser controls exposed. All you see in the DOM is the `<video>` element, but it contains a series of buttons and other controls inside its shadow DOM. The shadow DOM spec enables you to manipulate the shadow DOM of your own custom elements.

Creating a shadow DOM
---------------------

### Imperatively with JavaScript

The following page contains two elements, a `<div>` element with an `id` of `"host"`, and a `<span>` element containing some text:

```html
<div id="host"></div>
<span>I'm not in the shadow DOM</span>
```

We're going to use the `"host"` element as the shadow host. We call `attachShadow()` on the host to create the shadow DOM, and can then add nodes to the shadow DOM just like we would to the main DOM. In this example we add a single `<span>` element:

```js
const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
const span = document.createElement("span");
span.textContent = "I'm in the shadow DOM";
shadow.appendChild(span);
```

The result looks like this:

### Declaratively with HTML

Creating a shadow DOM via JavaScript API might be a good option for client-side rendered applications. For other applications, a server-side rendered UI might have better performance and, therefore, a better user experience. In such cases, you can use the `<template>` element to declaratively define the shadow DOM. The key to this behavior is the enumerated `shadowrootmode` attribute, which can be set to either `open` or `closed`, the same values as the `mode` option of `attachShadow()` method.

```html
<div id="host">
  <template shadowrootmode="open">
    <span>I'm in the shadow DOM</span>
  </template>
</div>
```

**Note:** By default, contents of `<template>` are not displayed. In this case, because the `shadowrootmode="open"` was included, the shadow root is rendered. In supporting browsers, the visible contents within that shadow root are displayed.

After the browser parses the HTML, it replaces `<template>` element with its content wrapped in a shadow root that's attached to the parent element, the `<div id="host">` in our example. The resulting DOM tree looks like this:
```

- DIV id="host"
  - #shadow-root
    - SPAN
      - #text: I'm in the shadow DOM
```

Note that there's no `<template>` element in the DOM tree.

Encapsulation from JavaScript
-----------------------------

So far this might not look like much. But let's see what happens if code running in the page tries to access elements in the shadow DOM.

This page is just like the last one, except we've added two `<button>` elements.

```html
<div id="host"></div>
<span>I'm not in the shadow DOM</span>
<br />

<button id="upper" type="button">Uppercase span elements</button>
<button id="reload" type="button">Reload</button>
```

 Clicking the "Uppercase span elements" button finds all `<span>` elements in the page and changes their text to uppercase.
 Clicking the "Reload" button just reloads the page, so you can try again.

```js
const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
const span = document.createElement("span");
span.textContent = "I'm in the shadow DOM";
shadow.appendChild(span);

const upper = document.querySelector("button#upper");
upper.addEventListener("click", () => {
  const spans = Array.from(document.querySelectorAll("span"));
  for (const span of spans) {
    span.textContent = span.textContent.toUpperCase();
  }
});

const reload = document.querySelector("#reload");
reload.addEventListener("click", () => document.location.reload());
```

If you click "Uppercase span elements", you'll see that `Document.querySelectorAll()` doesn't find the elements in our shadow DOM: they are effectively hidden from JavaScript in the page:

Element.shadowRoot and the "mode" option
----------------------------------------

In the example above, we pass an argument `{ mode: "open" }` to `attachShadow()`. With `mode` set to `"open"`, the JavaScript in the page is able to access the internals of your shadow DOM through the `shadowRoot` property of the shadow host.

In this example, as before, the HTML contains the shadow host, a `<span>` element in the main DOM tree, and two buttons:

```html
<div id="host"></div>
<span>I'm not in the shadow DOM</span>
<br />

<button id="upper" type="button">Uppercase shadow DOM span elements</button>
<button id="reload" type="button">Reload</button>
```

This time the "Uppercase" button uses `shadowRoot` to find the `<span>` elements in the DOM:

```js
const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
const span = document.createElement("span");
span.textContent = "I'm in the shadow DOM";
shadow.appendChild(span);

const upper = document.querySelector("button#upper");
upper.addEventListener("click", () => {
  const spans = Array.from(host.shadowRoot.querySelectorAll("span"));
  for (const span of spans) {
    span.textContent = span.textContent.toUpperCase();
  }
});

const reload = document.querySelector("#reload");
reload.addEventListener("click", () => document.location.reload());
```

This time, the JavaScript running in the page can access the shadow DOM internals:

The `{mode: "open"}` argument gives the page a way to break the encapsulation of your shadow DOM. If you don't want to give the page this ability, pass `{mode: "closed"}` instead, and then `shadowRoot` returns `null`.

However, you should not consider this a strong security mechanism, because there are ways it can be evaded, for example by browser extensions running in the page. It's more of an indication that the page should not access the internals of your shadow DOM tree.

Encapsulation from CSS
----------------------

In this version of the page, the HTML is the same as the original:

```html
<div id="host"></div>
<span>I'm not in the shadow DOM</span>
```

In the JavaScript, we create the shadow DOM:

```js
const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
const span = document.createElement("span");
span.textContent = "I'm in the shadow DOM";
shadow.appendChild(span);
```

This time, we'll have some CSS targeting `<span>` elements in the page:

```css
span {
  color: blue;
  border: 1px solid black;
}
```

The page CSS does not affect nodes inside the shadow DOM:

Applying styles inside the shadow DOM
-------------------------------------

In this section we'll look at two different ways to apply styles inside a shadow DOM tree:

* *Programmatically*, by constructing a `CSSStyleSheet` object and attaching it to the shadow root.
* *Declaratively*, by adding a `<style>` element in a `<template>` element's declaration.

In both cases, the styles defined in the shadow DOM tree are scoped to that tree, so just as page styles don't affect elements in the shadow DOM, shadow DOM styles don't affect elements in the rest of the page.

### Constructable stylesheets

To style page elements in the shadow DOM with constructable stylesheets, we can:

1. Create an empty `CSSStyleSheet` object
2. Set its content using `CSSStyleSheet.replace()` or `CSSStyleSheet.replaceSync()`
3. Add it to the shadow root by assigning it to `ShadowRoot.adoptedStyleSheets`

Rules defined in the `CSSStyleSheet` will be scoped to the shadow DOM tree, as well as any other DOM trees to which we have assigned it.

Here, again, is the HTML containing our host and a `<span>`:

```html
<div id="host"></div>
<span>I'm not in the shadow DOM</span>
```

This time we will create the shadow DOM and assign a `CSSStyleSheet` object to it:

```js
const sheet = new CSSStyleSheet();
sheet.replaceSync("span { color: red; border: 2px dotted black;}");

const host = document.querySelector("#host");

const shadow = host.attachShadow({ mode: "open" });
shadow.adoptedStyleSheets = [sheet];

const span = document.createElement("span");
span.textContent = "I'm in the shadow DOM";
shadow.appendChild(span);
```

The styles defined in the shadow DOM tree are not applied in the rest of the page:

### Adding `<style>` elements in `<template>` declarations

An alternative to constructing `CSSStyleSheet` objects is to include a `<style>` element inside the `<template>` element used to define a web component.

In this case the HTML includes the `<template>` declaration

```html
<template id="my-element">
  <style>
 span {
 color: red;
 border: 2px dotted black;
 }
 </style>
  <span>I'm in the shadow DOM</span>
</template>

<div id="host"></div>
<span>I'm not in the shadow DOM</span>
```

In the JavaScript, we will create the shadow DOM and add the content of the `<template>` to it:

```js
const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
const template = document.getElementById("my-element");

shadow.appendChild(template.content);
```

Again, the styles defined in the `<template>` are applied only within the shadow DOM tree, and not in the rest of the page:

### Choosing between programmatic and declarative options

Which of these options to use is dependent on your application and personal preference.

Creating a `CSSStyleSheet` and assigning it to the shadow root using `adoptedStyleSheets` allows you to create a single stylesheet and share it among many DOM trees. For example, a component library could create a single stylesheet and then share it among all the custom elements belonging to that library. The browser will parse that stylesheet once. Also, you can make dynamic changes to the stylesheet and have them propagate to all components that use the sheet.

The approach of attaching a `<style>` element is great if you want to be declarative, have few styles, and don't need to share styles across different components.

Shadow DOM and custom elements
------------------------------

Without the encapsulation provided by shadow DOM, custom elements would be impossibly fragile. It would be too easy for a page to accidentally break a custom element's behavior or layout by running some page JavaScript or CSS. As a custom element developer, you'd never know whether the selectors applicable inside your custom element conflicted with those that applied in a page that chose to use your custom element.

Custom elements are implemented as a class which extends either the base `HTMLElement` or a built-in HTML element such as `HTMLParagraphElement`. Typically, the custom element itself is a shadow host, and the element creates multiple elements under that root, to provide the internal implementation of the element.

The example below creates a `<filled-circle>` custom element that just renders a circle filled with a solid color.

```js
class FilledCircle extends HTMLElement {
  constructor() {
    super();
  }
  connectedCallback() {
    // Create a shadow root
    // The custom element itself is the shadow host
    const shadow = this.attachShadow({ mode: "open" });

    // create the internal implementation
    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    const circle = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "circle",
    );
    circle.setAttribute("cx", "50");
    circle.setAttribute("cy", "50");
    circle.setAttribute("r", "50");
    circle.setAttribute("fill", this.getAttribute("color"));
    svg.appendChild(circle);

    shadow.appendChild(svg);
  }
}

customElements.define("filled-circle", FilledCircle);
```

```html
<filled-circle color="blue"></filled-circle>
```

For more examples, illustrating different aspects of custom element implementation, see our guide to custom elements.

See also
--------

* Using custom elements
* Using templates and slots
* `Element.attachShadow()`
* `ShadowRoot.adoptedStyleSheets`
* `CSSStyleSheet.replace()`
* `CSSStyleSheet.replaceSync()`
* `<template>`
* CSS scoping module
* `:host`
* `:host()`
* `:host-context()`
* `::slotted()`
* CSS shadow parts module
* `::part`


GPUBindGroupLayout: label property - Web APIs

GPUBindGroupLayout: label property
==================================

**Experimental:** **This is an experimental technology**
Check the Browser compatibility table carefully before using this in production.

 The **`label`** property of the
 `GPUBindGroupLayout` interface provides a label that can be used to identify the object, for example in `GPUError` messages or console warnings.

This can be set by providing a `label` property in the descriptor object passed into the originating `GPUDevice.createBindGroupLayout()` call, or you can get and set it directly on the `GPUBindGroupLayout` object.

Value
-----

A string. If this has not been previously set as described above, it will be an empty string.

Examples
--------

Setting and getting a label via `GPUBindGroupLayout.label`:

```js
// ...

const bindGroupLayout = device.createBindGroupLayout({
  entries: [
    {
      binding: 0,
      visibility: GPUShaderStage.COMPUTE,
      buffer: {
        type: "storage",
      },
    },
  ],
});

bindGroupLayout.label = "mybindgrouplayout";

console.log(bindGroupLayout.label); // "mybindgrouplayout";
```

Setting a label via the originating `GPUDevice.createBindGroupLayout()` call, and then getting it via `GPUBindGroupLayout.label`:

```js
// ...

const bindGroupLayout = device.createBindGroupLayout({
  entries: [
    {
      binding: 0,
      visibility: GPUShaderStage.COMPUTE,
      buffer: {
        type: "storage",
      },
    },
  ],
  label: "mybindgrouplayout",
});

console.log(bindGroupLayout.label); // "mybindgrouplayout";
```

Specifications
--------------

| Specification |
| --- |
| WebGPU # dom-gpuobjectbase-label |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* The WebGPU API


MessageEvent: ports property - Web APIs

MessageEvent: ports property
============================

 The **`ports`** read-only property of the
 `MessageEvent` interface is an array of `MessagePort` objects
 representing the ports associated with the channel the message is being sent through
 (where appropriate, e.g. in channel messaging or when sending a message to a shared
 worker).

Value
-----

An array of `MessagePort` objects.

Examples
--------

```js
onconnect = (e) => {
  const port = e.ports[0];

  port.addEventListener("message", (e) => {
    const workerResult = `Result: ${e.data[0] \* e.data[1]}`;
    port.postMessage(workerResult);
  });

  port.start(); // Required when using addEventListener. Otherwise called implicitly by onmessage setter.
};
```

Specifications
--------------

| Specification |
| --- |
| HTML Standard # dom-messageevent-ports-dev |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `ExtendableMessageEvent` — similar to this interface but used in
 interfaces that needs to give more flexibility to authors.


MessageEvent: origin property - Web APIs

MessageEvent: origin property
=============================

 The **`origin`** read-only property of the
 `MessageEvent` interface is a string representing the
 origin of the message emitter.

Value
-----

A string representing the origin.

Examples
--------

```js
myWorker.onmessage = (e) => {
  result.textContent = e.data;
  console.log("Message received from worker");
  console.log(e.origin);
};
```

Specifications
--------------

| Specification |
| --- |
| HTML Standard # dom-messageevent-origin-dev |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `ExtendableMessageEvent` — similar to this interface but used in
 interfaces that needs to give more flexibility to authors.


MessageEvent: MessageEvent() constructor - Web APIs

MessageEvent: MessageEvent() constructor
========================================

The **`MessageEvent()`** constructor creates a new `MessageEvent` object.

Syntax
------

```js
new MessageEvent(type)
new MessageEvent(type, options)
```

### Parameters

`type`

 A string with the name of the event.
 It is case-sensitive and browsers always set it to `message`.

`options` Optional

An object that, *in addition of the properties defined in `Event()`*, can have the following properties:

`data` Optional

 The data you want contained in the MessageEvent.
 This can be of any data type, and will default to `null` if not specified.

`origin` Optional

 A string representing the origin of the message emitter.
 This defaults to an empty string (`''`) if not specified.

`lastEventId` Optional

 A string representing a unique ID for the event.
 This defaults to an empty string ("") if not specified.

`source` Optional

 A `MessageEventSource` (which can be a `Window`, a `MessagePort`,
 or a `ServiceWorker` object) representing the message emitter.
 This defaults to `null` if not set.

`ports` Optional

 An array of `MessagePort` objects representing
 the ports associated with the channel the message is being sent through where appropriate
 (E.g. in channel messaging or when sending a message to a shared worker).
 This defaults to an empty array (`[]`) if not specified.

Return value
------------

A new `MessageEvent` object.

Examples
--------

```js
const myMessage = new MessageEvent("message", {
  data: "hello",
});
```

Specifications
--------------

| Specification |
| --- |
| HTML Standard # the-messageevent-interface:dom-event-constructor |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `ExtendableMessageEvent` — similar to this interface but used in interfaces that needs to give more flexibility to authors.


MessageEvent: lastEventId property - Web APIs

MessageEvent: lastEventId property
==================================

 The **`lastEventId`** read-only property of the
 `MessageEvent` interface is a string representing a
 unique ID for the event.

Value
-----

A string representing the ID.

Examples
--------

```js
myWorker.onmessage = (e) => {
  result.textContent = e.data;
  console.log("Message received from worker");
  console.log(e.lastEventId);
};
```

Specifications
--------------

| Specification |
| --- |
| HTML Standard # dom-messageevent-lasteventid-dev |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `ExtendableMessageEvent` — similar to this interface but used in
 interfaces that needs to give more flexibility to authors.


MessageEvent: data property - Web APIs

MessageEvent: data property
===========================

 The **`data`** read-only property of the
 `MessageEvent` interface represents the data sent by the message emitter.

Value
-----

The data sent by the message emitter; this can be any data type, depending on what originated this event.

Examples
--------

```js
myWorker.onmessage = (e) => {
  result.textContent = e.data;
  console.log("Message received from worker");
};
```

Specifications
--------------

| Specification |
| --- |
| HTML Standard # dom-messageevent-data-dev |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `ExtendableMessageEvent` — similar to this interface but used in
 interfaces that needs to give more flexibility to authors.


MessageEvent: source property - Web APIs

MessageEvent: source property
=============================

 The **`source`** read-only property of the
 `MessageEvent` interface is a `MessageEventSource` (which can be
 a WindowProxy, `MessagePort`, or
 `ServiceWorker` object) representing the message emitter.

Value
-----

 a `MessageEventSource` (which can be a WindowProxy,
 `MessagePort`, or `ServiceWorker` object) representing the
 message emitter.

Examples
--------

```js
myWorker.onmessage = (e) => {
  result.textContent = e.data;
  console.log("Message received from worker");
  console.log(e.source);
};
```

Specifications
--------------

| Specification |
| --- |
| HTML Standard # dom-messageevent-source-dev |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `ExtendableMessageEvent` — similar to this interface but used in
 interfaces that needs to give more flexibility to authors.


StorageManager: getDirectory() method - Web APIs

StorageManager: getDirectory() method
=====================================

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

The **`getDirectory()`** method of the `StorageManager` interface is used to obtain a reference to a `FileSystemDirectoryHandle` object allowing access to a directory and its contents, stored in the origin private file system (OPFS).

**Note:** This feature is available in Web Workers

Syntax
------

```js
getDirectory()
```

### Parameters

None.

### Return value

A `Promise` that fulfills with a `FileSystemDirectoryHandle` object.

### Exceptions

`SecurityError` `DOMException`

Thrown if the user agent is not able to map the requested directory to the local OPFS.

Examples
--------

The following asynchronous event handler function is contained inside a Web Worker. On receiving a message from the main thread it:

1. Gets a `FileSystemDirectoryHandle` representing the root of the OPFS using `getDirectory()`, storing it in the `root` variable.
2. Gets a file handle using `FileSystemDirectoryHandle.getFileHandle()`.
3. Creates a synchronous file access handle using `FileSystemFileHandle.createSyncAccessHandle()`.
4. Gets the size of the file and creates an `ArrayBuffer` to contain it.
5. Reads and writes to the file.
6. Persists the changes to disk and closes the synchronous access handle.

```js
onmessage = async (e) => {
  // Retrieve message sent to work from main script
  const message = e.data;

  // Get handle to draft file
  const root = await navigator.storage.getDirectory();
  const draftHandle = await root.getFileHandle("draft.txt", { create: true });
  // Get sync access handle
  const accessHandle = await draftHandle.createSyncAccessHandle();

  // Get size of the file.
  const fileSize = accessHandle.getSize();
  // Read file content to a buffer.
  const buffer = new DataView(new ArrayBuffer(fileSize));
  const readBuffer = accessHandle.read(buffer, { at: 0 });

  // Write the message to the end of the file.
  const encoder = new TextEncoder();
  const encodedMessage = encoder.encode(message);
  const writeBuffer = accessHandle.write(encodedMessage, { at: readBuffer });

  // Persist changes to disk.
  accessHandle.flush();

  // Always close FileSystemSyncAccessHandle if done.
  accessHandle.close();
};
```

**Note:** In earlier versions of the spec, `close()`, `flush()`, `getSize()`, and `truncate()` were wrongly specified as asynchronous methods, and older versions of some browsers implement them in this way. However, all current browsers that support these methods implement them as synchronous methods.

Specifications
--------------

| Specification |
| --- |
| File System Standard # dom-storagemanager-getdirectory |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `StorageManager`
* `navigator.storage`
* `FileSystemDirectoryHandle`


StorageManager: persisted() method - Web APIs

StorageManager: persisted() method
==================================

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

The **`persisted()`** method of the `StorageManager` interface returns a `Promise` that resolves to `true` if your site's storage bucket is persistent.

**Note:** This feature is available in Web Workers

Syntax
------

```js
persisted()
```

### Parameters

None.

### Returns

A `Promise` that resolves to a `Boolean`.

### Exceptions

`TypeError`

Thrown if obtaining a local storage shelf failed. For example, if the current origin is an opaque origin or if the user has disabled storage.

Example
-------

```js
if (navigator.storage && navigator.storage.persist) {
  navigator.storage.persisted().then((persistent) => {
    if (persistent) {
      console.log("Storage will not be cleared except by explicit user action");
    } else {
      console.log("Storage may be cleared by the UA under storage pressure.");
    }
  });
}
```

Specifications
--------------

| Specification |
| --- |
| Storage Standard # dom-storagemanager-persisted |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


StorageManager: persist() method - Web APIs

StorageManager: persist() method
================================

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

The **`persist()`** method of the `StorageManager` interface requests permission to use persistent storage, and returns a `Promise` that resolves to `true` if permission is granted and bucket mode is persistent, and `false` otherwise.

**Note:** This method is not available in Web Workers, though the `StorageManager` interface is.

Syntax
------

```js
persist()
```

### Parameters

None.

### Return value

A `Promise` that resolves to a `Boolean`.

### Exceptions

`TypeError`

Thrown if obtaining a local storage shelf failed. For example, if the current origin is an opaque origin or if the user has disabled storage.

Example
-------

```js
if (navigator.storage && navigator.storage.persist) {
  navigator.storage.persist().then((persistent) => {
    if (persistent) {
      console.log("Storage will not be cleared except by explicit user action");
    } else {
      console.log("Storage may be cleared by the UA under storage pressure.");
    }
  });
}
```

Specifications
--------------

| Specification |
| --- |
| Storage Standard # ref-for-dom-storagemanager-persist |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


StorageManager: estimate() method - Web APIs

StorageManager: estimate() method
=================================

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

The **`estimate()`** method of the `StorageManager` interface asks the Storage Manager for how much storage the current origin takes up (`usage`), and how much space is available (`quota`).

This method operates asynchronously, so it returns a `Promise` which resolves once the information is available. The promise's fulfillment handler is called with an object containing the usage and quota data.

**Note:** This feature is available in Web Workers

Syntax
------

```js
estimate()
```

### Parameters

None.

### Return value

A `Promise` that resolves to an object with the following properties:

`quota`

A numeric value in bytes which provides a conservative approximation of the total storage the user's device or computer has available for the site origin or Web app. It's possible that there's more than this amount of space available though you can't rely on that being the case.

`usage`

A numeric value in bytes approximating the amount of storage space currently being used by the site or Web app, out of the available space as indicated by `quota`. Unit is byte.

`usageDetails`
Non-standard

An object containing a breakdown of `usage` by storage system. All included properties will have a `usage` greater than 0 and any storage system with 0 `usage` will be excluded from the object.

**Note:** The returned values are not exact: between compression, deduplication, and obfuscation for security reasons, they will be imprecise.

You may find that the `quota` varies from origin to origin. This variance is based on factors such as:

* How often the user visits
* Public site popularity data
* User engagement signals like bookmarking, adding to homescreen, or accepting push notifications
### Exceptions

`TypeError`

Thrown if obtaining a local storage shelf failed. For example, if the current origin is an opaque origin or if the user has disabled storage.

Examples
--------

In this example, we obtain the usage estimates and present the percentage of storage capacity currently used to the user.

### HTML

```html
<label>
  You're currently using about <output id="percent"> </output>% of your
  estimated storage quota (<output id="quota"></output>).
</label>
```

### JavaScript

```js
navigator.storage.estimate().then((estimate) => {
  document.getElementById("percent").value = (
    (estimate.usage / estimate.quota) \*
    100
  ).toFixed(2);
  document.getElementById("quota").value =
    (estimate.quota / 1024 / 1024).toFixed(2) + "MB";
});
```

### Result

Specifications
--------------

| Specification |
| --- |
| Storage Standard # ref-for-dom-storagemanager-estimate |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* Storage API
* `Storage`, the object returned by `Window.localStorage`
* `StorageManager`
* `navigator.storage`


Multi-touch interaction - Web APIs

Multi-touch interaction
=======================

The touch event interfaces support application-specific single and multi-touch interactions. However, the interfaces can be a bit tricky for programmers to use because touch events are very different from other DOM input events, such as `mouse events`. The application described in this guide shows how to use touch events for simple single and multi-touch interactions, the basics needed to build application-specific gestures.

A *live* version of this application is available on GitHub. The source code is available on GitHub and pull requests and bug reports are welcome.

Example
-------

This example demonstrates using the `touchstart`, `touchmove`, `touchcancel`, and `touchend`) touch events for the following gestures: single touch, two (simultaneous) touches, more than two simultaneous touches, 1-finger swipe, and 2-finger move/pinch/swipe.

### Define touch targets

The application uses `<div>` elements to represent four touch areas.

```html
<style>
 div {
 margin: 0em;
 padding: 2em;
 }
 #target1 {
 background: white;
 border: 1px solid black;
 }
 #target2 {
 background: white;
 border: 1px solid black;
 }
 #target3 {
 background: white;
 border: 1px solid black;
 }
 #target4 {
 background: white;
 border: 1px solid black;
 }
</style>
```

### Global state

`tpCache` is used to cache touch points for processing outside of the event where they were fired.

```js
// Log events flag
const logEvents = false;

// Touch Point cache
const tpCache = [];
```

### Register event handlers

Event handlers are registered for all four touch event types. The `touchend` and `touchcancel` event types use the same handler.

```js
function set\_handlers(name) {
  // Install event handlers for the given element
  const el = document.getElementById(name);
  el.ontouchstart = start_handler;
  el.ontouchmove = move_handler;
  // Use same handler for touchcancel and touchend
  el.ontouchcancel = end_handler;
  el.ontouchend = end_handler;
}

function init() {
  set\_handlers("target1");
  set\_handlers("target2");
  set\_handlers("target3");
  set\_handlers("target4");
}
```

### Move/Pinch/Zoom handler

This function provides very basic support for 2-touch horizontal move/pinch/zoom handling. The code does not include error handling, or vertical moving. Note that the *threshold* for pinch and zoom movement detection is application specific (and device dependent).

```js
// This is a very basic 2-touch move/pinch/zoom handler that does not include
// error handling, only handles horizontal moves, etc.
function handle\_pinch\_zoom(ev) {
  if (ev.targetTouches.length === 2 && ev.changedTouches.length === 2) {
    // Check if the two target touches are the same ones that started
    // the 2-touch
    const point1 = tpCache.findLastIndex(
      (tp) => tp.identifier === ev.targetTouches[0].identifier,
    );
    const point2 = tpCache.findLastIndex(
      (tp) => tp.identifier === ev.targetTouches[1].identifier,
    );

    if (point1 >= 0 && point2 >= 0) {
      // Calculate the difference between the start and move coordinates
      const diff1 = Math.abs(
        tpCache[point1].clientX - ev.targetTouches[0].clientX,
      );
      const diff2 = Math.abs(
        tpCache[point2].clientX - ev.targetTouches[1].clientX,
      );

      // This threshold is device dependent as well as application specific
      const PINCH\_THRESHOLD = ev.target.clientWidth / 10;
      if (diff1 >= PINCH\_THRESHOLD && diff2 >= PINCH\_THRESHOLD)
        ev.target.style.background = "green";
    } else {
      // empty tpCache
      tpCache = [];
    }
  }
}
```

### Touch start handler

The `touchstart` event handler caches touch points to support 2-touch gestures. It also calls `preventDefault()` to keep the browser from applying further event handling (for example, mouse event emulation).

```js
function start\_handler(ev) {
  // If the user makes simultaneous touches, the browser will fire a
  // separate touchstart event for each touch point. Thus if there are
  // three simultaneous touches, the first touchstart event will have
  // targetTouches length of one, the second event will have a length
  // of two, and so on.
  ev.preventDefault();
  // Cache the touch points for later processing of 2-touch pinch/zoom
  if (ev.targetTouches.length === 2) {
    for (let i = 0; i < ev.targetTouches.length; i++) {
      tpCache.push(ev.targetTouches[i]);
    }
  }
  if (logEvents) log("touchStart", ev, true);
  update\_background(ev);
}
```

### Touch move handler

The `touchmove` handler calls `preventDefault()` for the same reason mentioned above, and invokes the pinch/zoom handler.

```js
function move\_handler(ev) {
  // Note: if the user makes more than one "simultaneous" touches, most browsers
  // fire at least one touchmove event and some will fire several touchmoves.
  // Consequently, an application might want to "ignore" some touchmoves.
  //
  // This function sets the target element's border to "dashed" to visually
  // indicate the target received a move event.
  //
  ev.preventDefault();
  if (logEvents) log("touchMove", ev, false);
  // To avoid too much color flashing many touchmove events are started,
  // don't update the background if two touch points are active
  if (!(ev.touches.length === 2 && ev.targetTouches.length === 2))
    update\_background(ev);

  // Set the target element's border to dashed to give a clear visual
  // indication the element received a move event.
  ev.target.style.border = "dashed";

  // Check this event for 2-touch Move/Pinch/Zoom gesture
  handle\_pinch\_zoom(ev);
}
```

### Touch end handler

The `touchend` handler restores the event target's background color back to its original color.

```js
function end\_handler(ev) {
  ev.preventDefault();
  if (logEvents) log(ev.type, ev, false);
  if (ev.targetTouches.length === 0) {
    // Restore background and border to original values
    ev.target.style.background = "white";
    ev.target.style.border = "1px solid black";
  }
}
```

### Application UI

The application uses `<div>` elements for the touch areas and provides buttons to enable logging and clear the log.

```html
<div id="target1">Tap, Hold or Swipe me 1</div>
<div id="target2">Tap, Hold or Swipe me 2</div>
<div id="target3">Tap, Hold or Swipe me 3</div>
<div id="target4">Tap, Hold or Swipe me 4</div>

<!-- UI for logging/debugging -->
<button id="log" onclick="enableLog(event);">Start/Stop event logging</button>
<button id="clearlog" onclick="clearLog(event);">Clear the log</button>
<p></p>
<output></output>
```

### Miscellaneous functions

These functions support the application but aren't directly involved with the event flow.

#### Update background color

The background color of the touch areas will change as follows: no touch is `white`; one touch is `yellow`; two simultaneous touches is `pink`, and three or more simultaneous touches is `lightblue`. See touch move for information about the background color changing when a 2-finger move/pinch/zoom is detected.

```js
function update\_background(ev) {
  // Change background color based on the number simultaneous touches
  // in the event's targetTouches list:
  // yellow - one tap (or hold)
  // pink - two taps
  // lightblue - more than two taps
  switch (ev.targetTouches.length) {
    case 1:
      // Single tap`
      ev.target.style.background = "yellow";
      break;
    case 2:
      // Two simultaneous touches
      ev.target.style.background = "pink";
      break;
    default:
      // More than two simultaneous touches
      ev.target.style.background = "lightblue";
  }
}
```

#### Event logging

The functions are used to log event activity to the application window, to support debugging and learning about the event flow.

```js
function enableLog(ev) {
  logEvents = !logEvents;
}

function log(name, ev, printTargetIds) {
  const o = document.getElementsByTagName("output")[0];
  let s =
    `${name}: touches = ${ev.touches.length} ; ` +
    `targetTouches = ${ev.targetTouches.length} ; ` +
    `changedTouches = ${ev.changedTouches.length}`;
  o.innerHTML += `${s}<br>`;

  if (printTargetIds) {
    s = "";
    for (let i = 0; i < ev.targetTouches.length; i++) {
      s += `... id = ${ev.targetTouches[i].identifier}<br>`;
    }
    o.innerHTML += s;
  }
}

function clearLog(event) {
  const o = document.getElementsByTagName("output")[0];
  o.innerHTML = "";
}
```

Related topics and resources
----------------------------

* `Pointer Events`


Using Touch Events - Web APIs

Using Touch Events
==================

Today, most Web content is designed for keyboard and mouse input. However, devices with touch screens (especially portable devices) are mainstream and Web applications can either directly process touch-based input by using `Touch Events` or the application can use *interpreted mouse events* for the application input. A disadvantage to using mouse events is that they do not support concurrent user input, whereas touch events support multiple simultaneous inputs (possibly at different locations on the touch surface), thus enhancing user experiences.

The touch events interfaces support application specific single and multi-touch interactions such as a two-finger gesture. A multi-touch interaction starts when a finger (or stylus) first touches the contact surface. Other fingers may subsequently touch the surface and optionally move across the touch surface. The interaction ends when the fingers are removed from the surface. During this interaction, an application receives touch events during the start, move, and end phases. The application may apply its own semantics to the touch inputs.

Interfaces
----------

Touch events consist of three interfaces (`Touch`, `TouchEvent` and `TouchList`) and the following event types:

* `touchstart` - fired when a touch point is placed on the touch surface.
* `touchmove` - fired when a touch point is moved along the touch surface.
* `touchend` - fired when a touch point is removed from the touch surface.
* `touchcancel` - fired when a touch point has been disrupted in an implementation-specific manner (for example, too many touch points are created).

The `Touch` interface represents a single contact point on a touch-sensitive device. The contact point is typically referred to as a *touch point* or just a *touch*. A touch is usually generated by a finger or stylus on a touchscreen, pen or trackpad. A touch point's properties include a unique identifier, the touch point's target element as well as the *X* and *Y* coordinates of the touch point's position relative to the viewport, page, and screen.

The `TouchList` interface represents a *list* of contact points with a touch surface, one touch point per contact. Thus, if the user activated the touch surface with one finger, the list would contain one item, and if the user touched the surface with three fingers, the list length would be three.

The `TouchEvent` interface represents an event sent when the state of contacts with a touch-sensitive surface changes. The state changes are starting contact with a touch surface, moving a touch point while maintaining contact with the surface, releasing a touch point and canceling a touch event. This interface's attributes include the state of several *modifier keys* (for example the `shift` key) and the following touch lists:

* `touches` - a list of all of the touch points currently on the screen.
* `targetTouches` - a list of the touch points on the *target* DOM element.
* `changedTouches` - a list of the touch points whose items depend on the associated event type:
    + For the `touchstart` event, it is a list of the touch points that became active with the current event.
    + For the `touchmove` event, it is a list of the touch points that have changed since the last event.
    + For the `touchend` event, it is a list of the touch points that have been removed from the surface (that is, the set of touch points corresponding to fingers no longer touching the surface).

Together, these interfaces define a relatively low-level set of features, yet they support many kinds of touch-based interaction, including the familiar multi-touch gestures such as multi-finger swipe, rotation, pinch and zoom.

From interfaces to gestures
---------------------------

An application may consider different factors when defining the semantics of a gesture. For instance, the distance a touch point traveled from its starting location to its location when the touch ended. Another potential factor is time; for example, the time elapsed between the touch's start and the touch's end, or the time lapse between two *consecutive* taps intended to create a double-tap gesture. The directionality of a swipe (for example left to right, right to left, etc.) is another factor to consider.

The touch list(s) an application uses depends on the semantics of the application's *gestures*. For example, if an application supports a single touch (tap) on one element, it would use the `targetTouches` list in the `touchstart` event handler to process the touch point in an application-specific manner. If an application supports two-finger swipe for any two touch points, it will use the `changedTouches` list in the `touchmove` event handler to determine if two touch points had moved and then implement the semantics of that gesture in an application-specific manner.

Browsers typically dispatch *emulated* mouse and click events when there is only a single active touch point. Multi-touch interactions involving two or more active touch points will usually only generate touch events. To prevent the emulated mouse events from being sent, use the `preventDefault()` method in the touch event handlers. If you want to interact with both mouse and touches, use Pointer events instead.

Basic steps
-----------

This section contains a basic usage of using the above interfaces. See the `Touch Events Overview` for a more detailed example.

Register an event handler for each touch event type.

```js
// Register touch event handlers
someElement.addEventListener("touchstart", process_touchstart, false);
someElement.addEventListener("touchmove", process_touchmove, false);
someElement.addEventListener("touchcancel", process_touchcancel, false);
someElement.addEventListener("touchend", process_touchend, false);
```

Process an event in an event handler, implementing the application's gesture semantics.

```js
// touchstart handler
function process\_touchstart(ev) {
  // Use the event's data to call out to the appropriate gesture handlers
  switch (ev.touches.length) {
    case 1:
      handle\_one\_touch(ev);
      break;
    case 2:
      handle\_two\_touches(ev);
      break;
    case 3:
      handle\_three\_touches(ev);
      break;
    default:
      gesture\_not\_supported(ev);
      break;
  }
}
```

Access the attributes of a touch point.

```js
// Create touchstart handler
someElement.addEventListener(
  "touchstart",
  (ev) => {
    // Iterate through the touch points that were activated
    // for this element and process each event 'target'
    for (let i = 0; i < ev.targetTouches.length; i++) {
      process\_target(ev.targetTouches[i].target);
    }
  },
  false,
);
```

Prevent the browser from processing *emulated mouse events*.

```js
// touchmove handler
function process\_touchmove(ev) {
  // Set call preventDefault()
  ev.preventDefault();
}
```

Best practices
--------------

Here are some *best practices* to consider when using touch events:

* Minimize the amount of work that is done in the touch handlers.
* Add the touch point handlers to the specific target element (rather than the entire document or nodes higher up in the document tree).
* Add `touchmove`, `touchend` and `touchcancel` event handlers within the `touchstart`.
* The target touch element or node should be large enough to accommodate a finger touch. If the target area is too small, touching it could result in firing other events for adjacent elements.
Implementation and deployment status
------------------------------------

The touch events browser compatibility data indicates touch event support among mobile browsers is relatively broad, with desktop browser support lagging although additional implementations are in progress.

Some new features regarding a touch point's touch area - the area of contact between the user and the touch surface - are in the process of being standardized. The new features include the *X* and *Y* radius of the ellipse that most closely circumscribes a touch point's contact area with the touch surface. The touch point's *rotation angle* - the number of degrees of rotation to apply to the described ellipse to align with the contact area - is also be standardized as is the amount of pressure applied to a touch point.

What about Pointer Events?
--------------------------

The introduction of new input mechanisms results in increased application complexity to handle various input events, such as key events, mouse events, pen/stylus events, and touch events. To help address this problem, the Pointer Events standard *defines events and related interfaces for handling hardware agnostic pointer input from devices including a mouse, pen, touchscreen, etc.*. That is, the abstract *pointer* creates a unified input model that can represent a contact point for a finger, pen/stylus or mouse. See the Pointer Events MDN article.

The pointer event model can simplify an application's input processing since a pointer represents input from any input device. Additionally, the pointer event types are very similar to mouse event types (for example, `pointerdown` and `pointerup`) thus code to handle pointer events closely matches mouse handling code.

The implementation status of pointer events in browsers is relatively high with Chrome, Firefox, IE11 and Edge having complete implementations.

Examples and demos
------------------

The following documents describe how to use touch events and include example code:

* `Touch Events Overview`
* Implement Custom Gestures
* Add touch screen support to your website (The easy way)

Touch event demonstrations:

* Paint Program (by Rick Byers)
* Touch/pointer tests and demos (by Patrick H. Lauke)
Community
---------

* Touch Events Community Group
* Mail list
* W3C #touchevents IRC channel
Related topics and resources
----------------------------

* Pointer Events Standard


XRMediaBinding: createCylinderLayer() method - Web APIs

XRMediaBinding: createCylinderLayer() method
============================================

**Experimental:** **This is an experimental technology**
Check the Browser compatibility table carefully before using this in production.

The **`createCylinderLayer()`** method of the `XRMediaBinding` interface returns an `XRCylinderLayer` object which is a layer that takes up a curved rectangular space in the virtual environment.

Syntax
------

```js
createCylinderLayer(video, options)
```

### Parameters

`video`

An `HTMLVideoElement` to display.

`options`

An object to configure the `XRCylinderLayer`. The object can have the following properties and `space` is required:

`aspectRatio` Optional

A number indicating the ratio of the visible cylinder section. It is the ratio of the width of the visible section of the cylinder divided by its height. The width is calculated by multiplying the `radius` with the `centralAngle`.

`centralAngle` Optional

A number indicating the angle in radians of the visible section of the cylinder. Default value: `0.78539` (π / 4).

`invertStereo` Optional

A boolean specifying if the natural location of each view in the video should be inverted. By default `false`.

`layout` Optional

A string indicating the layout of the video. Possible values:

`default`

The layer accommodates all views of the session.

`mono`

A single `XRSubImage` is allocated and presented to both eyes.

`stereo`

The user agent decides how it allocates the `XRSubImage` (one or two) and the layout (top/bottom or left/right). It is recommended to use the `texture-array` texture type for `stereo` layouts.

`stereo-left-right`

A single `XRSubImage` is allocated. Left eye gets the left area of the texture, right eye the right. This layout is designed to minimize draw calls for content that is already in stereo (for example stereo videos or images).

`stereo-top-bottom`

 A single `XRSubImage` is allocated. Left eye gets the top area of the texture, right eye the bottom. This layout is designed to minimize draw calls for content that is already in stereo (for example stereo videos or images).
 The default value is `mono`.

`radius` Optional

A number indicating the radius of the cylinder. Default value `2.0`.

`space` **Required**

An `XRSpace` object defining the layer's spatial relationship with the user's physical environment.

`transform` Optional

An `XRRigidTransform` object defining the offset and orientation relative to `space`.

### Return value

An `XRCylinderLayer` object.

Examples
--------

### Creating an `XRCylinderLayer` to display a video

Create an `XRMediaBinding` and use an `HTMLVideoElement` that is passed into `createCylinderLayer()`. Configure the quad layer using the options listed above and present the layer to the XR device by adding it to the `layers` render state in `XRSession.updateRenderState()`.

```js
function onXRSessionStarted(xrSession) {
  const xrMediaBinding = new XRMediaBinding(xrSession);
  const video = document.createElement("video");
  video.src = "just-fascination.mp4";

  const videoLayer = xrMediaBinding.createCylinderLayer(video, {
    space: xrReferenceSpace,
  });

  xrSession.updateRenderState({
    layers: [videoLayer],
  });
}
```

Specifications
--------------

| Specification |
| --- |
| WebXR Layers API Level 1 # dom-xrmediabinding-createcylinderlayer |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `XRCylinderLayer`


XRMediaBinding: createEquirectLayer() method - Web APIs

XRMediaBinding: createEquirectLayer() method
============================================

**Experimental:** **This is an experimental technology**
Check the Browser compatibility table carefully before using this in production.

The **`createEquirectLayer()`** method of the `XRMediaBinding` interface returns an `XREquirectLayer` object which is a layer that maps an equirectangular coded data onto the inside of a sphere.

Syntax
------

```js
createEquirectLayer(video, options)
```

### Parameters

`video`

An `HTMLVideoElement` to display.

`options`

An object to configure the `XREquirectLayer`. The object can have the following properties and `space` is required:

`centralHorizontalAngle` Optional

A number indicating the central horizontal angle in radians for the sphere. Default value: `6.28318` (2π).

`invertStereo` Optional

A boolean specifying if the natural location of each view in the video should be inverted. By default `false`.

`layout` Optional

A string indicating the layout of the video. Possible values:

`default`

The layer accommodates all views of the session.

`mono`

A single `XRSubImage` is allocated and presented to both eyes.

`stereo`

The user agent decides how it allocates the `XRSubImage` (one or two) and the layout (top/bottom or left/right). It is recommended to use the `texture-array` texture type for `stereo` layouts.

`stereo-left-right`

A single `XRSubImage` is allocated. Left eye gets the left area of the texture, right eye the right. This layout is designed to minimize draw calls for content that is already in stereo (for example stereo videos or images).

`stereo-top-bottom`

 A single `XRSubImage` is allocated. Left eye gets the top area of the texture, right eye the bottom. This layout is designed to minimize draw calls for content that is already in stereo (for example stereo videos or images).
 The default value is `mono`.

`lowerVerticalAngle` Optional

A number indicating the lower vertical angle in radians for the sphere. Default value: `-1.570795` (-π/2).

`radius` Optional

A number indicating the radius of the sphere. Default value `0.0`.

`space` **Required**

An `XRSpace` object defining the layer's spatial relationship with the user's physical environment.

`transform` Optional

An `XRRigidTransform` object defining the offset and orientation relative to `space`.

`upperVerticalAngle` Optional

A number indicating the upper vertical angle in radians for the sphere. Default value: `1.570795` (π/2).

### Return value

An `XREquirectLayer` object.

Examples
--------

### Creating an `XREquirectLayer` to display a video

Create an `XRMediaBinding` and use an `HTMLVideoElement` that is passed into `createEquirectLayer()`. Configure the quad layer using the options listed above and present the layer to the XR device by adding it to the `layers` render state in `XRSession.updateRenderState()`.

```js
function onXRSessionStarted(xrSession) {
  const xrMediaBinding = new XRMediaBinding(xrSession);
  const video = document.createElement("video");
  video.src = "just-fascination.mp4";

  const videoLayer = xrMediaBinding.createEquirectLayer(video, {
    space: xrReferenceSpace,
  });

  xrSession.updateRenderState({
    layers: [videoLayer],
  });
}
```

Specifications
--------------

| Specification |
| --- |
| WebXR Layers API Level 1 # dom-xrmediabinding-createequirectlayer |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `XREquirectLayer`


XRMediaBinding: XRMediaBinding() constructor - Web APIs

XRMediaBinding: XRMediaBinding() constructor
============================================

**Experimental:** **This is an experimental technology**
Check the Browser compatibility table carefully before using this in production.

The **`XRMediaBinding()`** constructor creates and returns a new `XRMediaBinding` object.

Syntax
------

```js
new XRMediaBinding(session)
```

### Parameters

`session`

An `XRSession` object specifying the WebXR session for which to create the media binding.

### Return value

A newly-created `XRMediaBinding`.

### Exceptions

`InvalidStateError` `DOMException`

Thrown if the new `XRMediaBinding` could not be created due to one of a number of possible state errors:

* The `XRSession` specified by `session` has already been stopped.
* The specified `session` is not immersive.

Examples
--------

### Creating a new `XRMediaBinding`

The following example creates a new media binding for a session to create an `XRQuadLayer` to display a video layer in the scene.

```js
const xrMediaBinding = new XRMediaBinding(xrSession);

const video = document.createElement("video");
video.src = "just-fascination.mp4";
const layer = xrMediaBinding.createQuadLayer(video);
```

Specifications
--------------

| Specification |
| --- |
| WebXR Layers API Level 1 # dom-xrmediabinding-xrmediabinding |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `XRWebGLBinding`


XRMediaBinding: createQuadLayer() method - Web APIs

XRMediaBinding: createQuadLayer() method
========================================

**Experimental:** **This is an experimental technology**
Check the Browser compatibility table carefully before using this in production.

The **`createQuadLayer()`** method of the `XRMediaBinding` interface returns an `XRQuadLayer` object which is a layer that takes up a flat rectangular space in the virtual environment.

Syntax
------

```js
createQuadLayer(video, options)
```

### Parameters

`video`

An `HTMLVideoElement` to display.

`options`

An object to configure the `XRQuadLayer`. The object can have the following properties and `space` is required:

`height` Optional

A number specifying the height of the layer.

`invertStereo` Optional

A boolean specifying if the natural location of each view in the video should be inverted. By default `false`.

`layout` Optional

A string indicating the layout of the video. Possible values:

`default`

The layer accommodates all views of the session.

`mono`

A single `XRSubImage` is allocated and presented to both eyes.

`stereo`

The user agent decides how it allocates the `XRSubImage` (one or two) and the layout (top/bottom or left/right). It is recommended to use the `texture-array` texture type for `stereo` layouts.

`stereo-left-right`

A single `XRSubImage` is allocated. Left eye gets the left area of the texture, right eye the right. This layout is designed to minimize draw calls for content that is already in stereo (for example stereo videos or images).

`stereo-top-bottom`

 A single `XRSubImage` is allocated. Left eye gets the top area of the texture, right eye the bottom. This layout is designed to minimize draw calls for content that is already in stereo (for example stereo videos or images).
 The default value is `mono`.

`space` **Required**

An `XRSpace` object defining the layer's spatial relationship with the user's physical environment.

`transform` Optional

An `XRRigidTransform` object defining the offset and orientation relative to `space`.

`width` Optional

A number specifying the width of the layer.

### Return value

An `XRQuadLayer` object.

Examples
--------

### Creating an `XRQuadLayer` to display a video

Create an `XRMediaBinding` and use an `HTMLVideoElement` that is passed into `createQuadLayer()`. Configure the quad layer using the options listed above and present the layer to the XR device by adding it to the `layers` render state in `XRSession.updateRenderState()`.

```js
function onXRSessionStarted(xrSession) {
  const xrMediaBinding = new XRMediaBinding(xrSession);
  const video = document.createElement("video");
  video.src = "just-fascination.mp4";

  const videoLayer = xrMediaBinding.createQuadLayer(video, {
    space: xrReferenceSpace,
  });

  xrSession.updateRenderState({
    layers: [videoLayer],
  });
}
```

Specifications
--------------

| Specification |
| --- |
| WebXR Layers API Level 1 # dom-xrmediabinding-createquadlayer |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `XRQuadLayer`


CookieChangeEvent: CookieChangeEvent() constructor - Web APIs

CookieChangeEvent: CookieChangeEvent() constructor
==================================================

**Secure context:** This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

**Experimental:** **This is an experimental technology**
Check the Browser compatibility table carefully before using this in production.

 The **`CookieChangeEvent()`** constructor creates a new `CookieChangeEvent` object
 which is the event type of the `change` event fired at a `CookieStore` when any cookie changes occur.
 This constructor is called by the browser when a change event occurs.

**Note:** This event constructor is generally not needed for production websites. It's primary use is for tests that require an instance of this event.

Syntax
------

```js
new CookieChangeEvent(type)
new CookieChangeEvent(type, options)
```

### Parameters

`type`

A string with the name of the event. It is case-sensitive and browsers always set it to `change`.

`options` Optional

An object that, *in addition of the properties defined in `Event()`*, can have the following properties:

`changed`Optional

An array containing the changed cookies.

`deleted`Optional

An array containing the deleted cookies.

### Return value

A new `CookieChangeEvent` object.

Specifications
--------------

| Specification |
| --- |
| Cookie Store API # dom-cookiechangeevent-cookiechangeevent |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.