diff --git a/.nvmrc b/.nvmrc
index 53d1c14db376ead..54c65116f15a683 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-v22
+v24
diff --git a/files/en-us/learn_web_development/core/scripting/build_your_own_function/index.md b/files/en-us/learn_web_development/core/scripting/build_your_own_function/index.md
index 2039ecffc20a61c..540c6a5fa02f02d 100644
--- a/files/en-us/learn_web_development/core/scripting/build_your_own_function/index.md
+++ b/files/en-us/learn_web_development/core/scripting/build_your_own_function/index.md
@@ -78,9 +78,7 @@ To begin with, let's put together a basic function.
closeBtn.textContent = "x";
panel.appendChild(closeBtn);
- closeBtn.addEventListener("click", () =>
- panel.parentNode.removeChild(panel),
- );
+ closeBtn.addEventListener("click", () => body.removeChild(panel));
```
This is quite a lot of code to go through, so we'll walk you through it bit by bit.
@@ -117,10 +115,10 @@ panel.appendChild(closeBtn);
Finally, we call {{domxref("EventTarget/addEventListener", "addEventListener()")}} to add a function that will be called when the user clicks the "close" button. The code will delete the whole panel from the page — to close the message box.
-Briefly, the `addEventListener()` method is provided by the button (or in fact, any element on the page) that can be passed a function and the name of an event. In this case, the name of the event is 'click', meaning that when the user clicks the button, the function will run. You'll learn a lot more about events in our [events article](/en-US/docs/Learn_web_development/Core/Scripting/Events). The line inside the function uses the {{domxref("Node.removeChild()")}} DOM API function to specify that we want to remove a specific child element of the HTML element — in this case, the panel `
`.
+Briefly, the `addEventListener()` method can be called on any element on the page, and is usually passed two arguments: the name of an event and a function to run when the event occurs. In this case, the event name is `click`, meaning that when the user clicks the button, the function will run. You'll learn a lot more about events in our [events article](/en-US/docs/Learn_web_development/Core/Scripting/Events). The line inside the function uses the {{domxref("Node.removeChild()", "removeChild()")}} method to specify that we want to remove a specific child element of the `` element: in this case, the panel `
`.
```js
-closeBtn.addEventListener("click", () => panel.parentNode.removeChild(panel));
+closeBtn.addEventListener("click", () => body.removeChild(panel));
```
Basically, this whole block of code is generating a block of HTML that looks like so, and inserting it into the page:
diff --git a/files/en-us/mozilla/firefox/releases/144/index.md b/files/en-us/mozilla/firefox/releases/144/index.md
index c1f4cff6f57c132..d439daf0e0c203a 100644
--- a/files/en-us/mozilla/firefox/releases/144/index.md
+++ b/files/en-us/mozilla/firefox/releases/144/index.md
@@ -45,6 +45,7 @@ Firefox 144 was released on [October 14, 2025](https://whattrainisitnow.com/rele
- The [View Transition API](/en-US/docs/Web/API/View_Transition_API) is now supported for [SPAs (single-page applications)](/en-US/docs/Glossary/SPA). This provides a mechanism for easily creating animated transitions between different website views. ([Firefox bug 1985809](https://bugzil.la/1985809)).
- The {{domxref("CSSStyleProperties")}} interface of the [CSS Object Model (CSSOM)](/en-US/docs/Web/API/CSS_Object_Model) is now implemented (this was renamed from a non-standard interface `CSS2Properties`). The new interface is present but not yet used. ([Firefox bug 1919582](https://bugzil.la/1919582)).
- The {{domxref("PerformanceEventTiming.interactionId", "interactionId")}} property of the {{domxref("PerformanceEventTiming")}} interface is a unique identifier that associates related events belonging to a single user interaction. This can be used to calculate the {{glossary("Interaction to next paint")}} metric, which helps analyze responsiveness to user interaction over the lifetime of a page. ([Firefox bug 1956809](https://bugzil.la/1956809)).
+- The {{domxref("Navigation.navigate()")}} method of the {{domxref("Navigation API", "Navigation API", "", "nocode")}} no longer accepts URLs with a scheme of `javascript`. Calling `navigate()` with a `javascript:` URL now throws a `NotSupportedError` exception. ([Firefox bug 1981104](https://bugzil.la/1981104)).
#### DOM
diff --git a/files/en-us/web/api/customelementregistry/get/index.md b/files/en-us/web/api/customelementregistry/get/index.md
index 78f0ae9b89a1f04..cadd9bb0f04c8a7 100644
--- a/files/en-us/web/api/customelementregistry/get/index.md
+++ b/files/en-us/web/api/customelementregistry/get/index.md
@@ -34,16 +34,16 @@ customElements.define(
"my-paragraph",
class extends HTMLElement {
constructor() {
- let templateContent = document.getElementById("custom-paragraph").content;
+ const template = document.getElementById("custom-paragraph");
super() // returns element this scope
.attachShadow({ mode: "open" }) // sets AND returns this.shadowRoot
- .append(templateContent.cloneNode(true));
+ .append(document.importNode(template.content, true));
}
},
);
// Return a reference to the my-paragraph constructor
-let ctor = customElements.get("my-paragraph");
+const ctor = customElements.get("my-paragraph");
```
## Specifications
diff --git a/files/en-us/web/api/customelementregistry/getname/index.md b/files/en-us/web/api/customelementregistry/getname/index.md
index 723608cb9856103..95406aade6a9839 100644
--- a/files/en-us/web/api/customelementregistry/getname/index.md
+++ b/files/en-us/web/api/customelementregistry/getname/index.md
@@ -32,10 +32,10 @@ The name for the previously defined custom element, or `null` if there is no cus
```js
class MyParagraph extends HTMLElement {
constructor() {
- let templateContent = document.getElementById("custom-paragraph").content;
+ const template = document.getElementById("custom-paragraph");
super() // returns element this scope
.attachShadow({ mode: "open" }) // sets AND returns this.shadowRoot
- .append(templateContent.cloneNode(true));
+ .append(document.importNode(template.content, true));
}
}
diff --git a/files/en-us/web/api/document/importnode/index.md b/files/en-us/web/api/document/importnode/index.md
index c7c9cca89378a97..d3126bc8ea05384 100644
--- a/files/en-us/web/api/document/importnode/index.md
+++ b/files/en-us/web/api/document/importnode/index.md
@@ -8,17 +8,13 @@ browser-compat: api.Document.importNode
{{APIRef("DOM")}}
-The {{domxref("Document")}} object's **`importNode()`** method creates a copy of a
-{{domxref("Node")}} or {{domxref("DocumentFragment")}} from another document, to be
-inserted into the current document later.
+The **`importNode()`** method of the {{domxref("Document")}} interface creates a copy of a {{domxref("Node")}} or {{domxref("DocumentFragment")}} from another document, to be inserted into the current document later.
-The imported node is not yet included in the document tree. To include it, you need to
-call an insertion method such as {{domxref("Node.appendChild", "appendChild()")}} or
-{{domxref("Node.insertBefore", "insertBefore()")}} with a node that _is_
-currently in the document tree.
+The imported node is not yet included in the document tree. To include it, you need to call an insertion method such as {{domxref("Node.appendChild", "appendChild()")}} or {{domxref("Node.insertBefore", "insertBefore()")}} with a node that _is_ currently in the document tree.
-Unlike {{domxref("document.adoptNode()")}}, the original node is not removed from its
-original document. The imported node is a clone of the original.
+Unlike {{domxref("document.adoptNode()")}}, the original node is not removed from its original document. The imported node is a clone of the original.
+
+The {{domxref("Node.cloneNode()")}} method also creates a copy of a node. The difference is that `importNode()` clones the node in the context of the calling document, whereas `cloneNode()` uses the document of the node being cloned. The document context determines the {{domxref("CustomElementRegistry")}} for constructing any custom elements. For this reason, to clone nodes to be used in another document, use `importNode()` on the target document. The {{domxref("HTMLTemplateElement.content")}} is owned by a separate document, so it should also be cloned using `document.importNode()` so that custom element descendants are constructed using the definitions in the current document. See the {{domxref("Node.cloneNode()")}} page's examples for more details.
## Syntax
@@ -50,6 +46,8 @@ The copied `importedNode` in the scope of the importing document.
## Examples
+### Using importNode()
+
```js
const iframe = document.querySelector("iframe");
const oldNode = iframe.contentWindow.document.getElementById("myNode");
diff --git a/files/en-us/web/api/documentfragment/getelementbyid/index.md b/files/en-us/web/api/documentfragment/getelementbyid/index.md
index 56155f048ac231f..52aff4b716116f8 100644
--- a/files/en-us/web/api/documentfragment/getelementbyid/index.md
+++ b/files/en-us/web/api/documentfragment/getelementbyid/index.md
@@ -102,7 +102,7 @@ function displayStatus() {
while (fragmentViewer.hasChildNodes()) {
fragmentViewer.removeChild(fragmentViewer.lastChild);
}
- for (entry of fragment.children) {
+ for (const entry of fragment.children) {
fragmentViewer.appendChild(entry.cloneNode(true));
}
}
diff --git a/files/en-us/web/api/htmltemplateelement/content/index.md b/files/en-us/web/api/htmltemplateelement/content/index.md
index 0ad4c38a622126c..bf376c92eb8f23e 100644
--- a/files/en-us/web/api/htmltemplateelement/content/index.md
+++ b/files/en-us/web/api/htmltemplateelement/content/index.md
@@ -8,9 +8,9 @@ browser-compat: api.HTMLTemplateElement.content
{{APIRef("Web Components")}}
-The **`HTMLTemplateElement.content`** property returns a
-`` element's template contents (a
-{{domxref("DocumentFragment")}}).
+The **`content`** property of the {{domxref("HTMLTemplateElement")}} interface returns the `` element's template contents as a {{domxref("DocumentFragment")}}. This content's {{domxref("Node/ownerDocument", "ownerDocument")}} is a separate {{domxref("Document")}} from the one that contains the `` element itself — unless the containing document is itself constructed for the purpose of holding template content.
+
+The {{domxref("Node.cloneNode()")}} and {{domxref("Document.importNode()")}} methods both create a copy of a node. The difference is that `importNode()` clones the node in the context of the calling document, whereas `cloneNode()` uses the document of the node being cloned. The document context determines the {{domxref("CustomElementRegistry")}} for constructing any custom elements. For this reason, use `document.importNode()` to clone the `content` fragment so that custom element descendants are constructed using the definitions in the current document, rather than the separate document that owns the template content. See the {{domxref("Node.cloneNode()")}} page's examples for more details.
## Value
@@ -18,9 +18,31 @@ A {{domxref("DocumentFragment")}}.
## Examples
+### Using importNode() with template content
+
```js
const templateElement = document.querySelector("#foo");
-const documentFragment = templateElement.content.cloneNode(true);
+const documentFragment = document.importNode(templateElement.content, true);
+// Now you can insert the documentFragment into the DOM
+```
+
+### The ownerDocument of template content
+
+For `` elements created in the context of a normal HTML document, the `ownerDocument` of the `content` is a separate, freshly created document:
+
+```js
+const template = document.createElement("template");
+console.log(template.content.ownerDocument === document); // false
+console.log(template.content.ownerDocument.URL); // "about:blank"
+```
+
+If the `` element is created in the context of a document that itself was created for the purpose of holding template content, then the `ownerDocument` of the `content` is the same as that of the containing document:
+
+```js
+const template1 = document.createElement("template");
+const docForTemplate = template1.content.ownerDocument;
+const template2 = docForTemplate.createElement("template");
+console.log(template2.content.ownerDocument === docForTemplate); // true
```
## Specifications
diff --git a/files/en-us/web/api/intersection_observer_api/index.md b/files/en-us/web/api/intersection_observer_api/index.md
index 160ec557d5ebb7c..504fbdd8fd1ad38 100644
--- a/files/en-us/web/api/intersection_observer_api/index.md
+++ b/files/en-us/web/api/intersection_observer_api/index.md
@@ -600,11 +600,12 @@ To get a feeling for how thresholds work, try scrolling the box below around. Ea
let observers = [];
startup = () => {
- let wrapper = document.querySelector(".wrapper");
+ const wrapper = document.querySelector(".wrapper");
+ const template = document.querySelector("#boxTemplate");
// Options for the observers
- let observerOptions = {
+ const observerOptions = {
root: null,
rootMargin: "0px",
threshold: [],
@@ -615,7 +616,7 @@ startup = () => {
// since there will be so many of them (for each percentage
// point).
- let thresholdSets = [
+ const thresholdSets = [
[],
[0.5],
[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0],
@@ -629,12 +630,10 @@ startup = () => {
// Add each box, creating a new observer for each
for (let i = 0; i < 4; i++) {
- let template = document
- .querySelector("#boxTemplate")
- .content.cloneNode(true);
- let boxID = `box${i + 1}`;
- template.querySelector(".sampleBox").id = boxID;
- wrapper.appendChild(document.importNode(template, true));
+ const newBox = document.importNode(template.content, true);
+ const boxID = `box${i + 1}`;
+ newBox.querySelector(".sampleBox").id = boxID;
+ wrapper.appendChild(newBox);
// Set up the observer for this box
diff --git a/files/en-us/web/api/navigation/navigate/index.md b/files/en-us/web/api/navigation/navigate/index.md
index aa619567780da9a..7c775048ba4a19c 100644
--- a/files/en-us/web/api/navigation/navigate/index.md
+++ b/files/en-us/web/api/navigation/navigate/index.md
@@ -23,7 +23,7 @@ navigate(url, options)
### Parameters
- `url`
- - : The destination URL to navigate to. Note that when calling `navigate()` on another window's `navigation` object, the URL will be resolved relative to the target window's URL, not the calling window's URL. This matches the behavior of the [History API](/en-US/docs/Web/API/History_API), but not the behavior of the [Location API](/en-US/docs/Web/API/Location).
+ - : The destination URL to navigate to. Note that when calling `navigate()` on another window's `navigation` object, the URL will be resolved relative to the target window's URL, not the calling window's URL. This matches the behavior of the [History API](/en-US/docs/Web/API/History_API), but not the behavior of the [Location API](/en-US/docs/Web/API/Location). Note also that `javascript:` URLs are not allowed for security reasons.
- `options` {{optional_inline}}
- : An options object containing the following properties:
- `state` {{optional_inline}}
@@ -50,12 +50,14 @@ Either one of these promises rejects if the navigation has failed for some reaso
### Exceptions
- `DataCloneError` {{domxref("DOMException")}}
- - : Thrown if the `state` parameter had values included in it that are not structured-cloneable.
+ - : Thrown if the `state` parameter contains values that are not structured-cloneable.
+- `InvalidStateError` {{domxref("DOMException")}}
+ - : Thrown if the document is not currently active.
- `SyntaxError` {{domxref("DOMException")}}
- : Thrown if the `url` parameter is not a valid URL.
- `NotSupportedError` {{domxref("DOMException")}}
- - : Thrown if the `history` option is set to `push`, and any of the following special circumstances are true:
- - The browser is currently showing the initial `about:blank` document.
+ - : Thrown if:
+ - The `history` option is set to `push`, and the browser is currently showing the initial `about:blank` document.
- The `url`'s scheme is `javascript`.
## Examples
diff --git a/files/en-us/web/api/node/clonenode/index.md b/files/en-us/web/api/node/clonenode/index.md
index 911af7b251786ae..c15842f10a6aa35 100644
--- a/files/en-us/web/api/node/clonenode/index.md
+++ b/files/en-us/web/api/node/clonenode/index.md
@@ -8,28 +8,16 @@ browser-compat: api.Node.cloneNode
{{APIRef("DOM")}}
-The **`cloneNode()`** method of the {{domxref("Node")}} interface
-returns a duplicate of the node on which this method was called.
-Its parameter controls if the subtree contained in a node is also cloned or not.
+The **`cloneNode()`** method of the {{domxref("Node")}} interface returns a duplicate of the node on which this method was called. Its parameter controls if the subtree contained in the node is also cloned or not.
-Cloning a node copies all of its attributes and their values,
-including intrinsic (inline) listeners. It does _not_ copy event listeners added
-using [`addEventListener()`](/en-US/docs/Web/API/EventTarget/addEventListener) or
-those assigned to element properties (e.g., `node.onclick = someFunction`).
-Additionally, for a {{HTMLElement("canvas")}} element, the painted image is not copied.
+By default, cloning a node copies all of its attributes and their values, including event listeners specified via attributes. By setting the `deep` parameter, you can also copy the subtree contained in the node. It does _not_ copy any other internal data, such as event listeners added using [`addEventListener()`](/en-US/docs/Web/API/EventTarget/addEventListener) or `onevent` properties (e.g., `node.onclick = someFunction`), or the painted image for a {{HTMLElement("canvas")}} element.
+
+The {{domxref("Document.importNode()")}} method also creates a copy of a node. The difference is that `importNode()` clones the node in the context of the calling document, whereas `cloneNode()` uses the document of the node being cloned. The document context determines the {{domxref("CustomElementRegistry")}} for constructing any custom elements. For this reason, to clone nodes to be used in another document, use `importNode()` on the target document. The {{domxref("HTMLTemplateElement.content")}} is owned by a separate document, so it should also be cloned using `document.importNode()` so that custom element descendants are constructed using the definitions in the current document.
> [!WARNING]
-> `cloneNode()` may lead to duplicate element IDs in a document!
->
-> If the original node has an `id` attribute, and the clone
-> will be placed in the same document, then you should modify the clone's ID to be
-> unique.
+> `cloneNode()` may lead to duplicate element IDs in a document! If the original node has an `id` attribute, and the clone will be placed in the same document, then you should modify the clone's ID to be unique.
>
-> Also, `name` attributes may need to be modified,
-> depending on whether duplicate names are expected.
-
-To clone a node to insert into a _different_ document, use
-{{domxref("Document.importNode()")}} instead.
+> Also, `name` attributes may need to be modified, depending on whether duplicate names are expected.
## Syntax
@@ -60,11 +48,45 @@ using {{domxref("Node.appendChild()")}} or a similar method.
## Example
+### Using cloneNode()
+
```js
const p = document.getElementById("para1");
const p2 = p.cloneNode(true);
```
+### Using cloneNode() with templates
+
+Avoid using `cloneNode()` on the content of a {{htmlelement("template")}} element, because if the template contains custom elements, they will not be upgraded until they are inserted into the document.
+
+```js
+class MyElement extends HTMLElement {
+ constructor() {
+ super();
+ console.log("MyElement created");
+ }
+}
+customElements.define("my-element", MyElement);
+
+const template = document.createElement("template");
+template.innerHTML = ``;
+
+const clone = template.content.cloneNode(true);
+// No log here; my-element is undefined in the template's document
+customElements.upgrade(clone);
+// Still no log; my-element is still undefined in the template's document
+document.body.appendChild(clone);
+// Logs "MyElement created"; my-element is now upgraded
+```
+
+Instead, use `document.importNode()` to clone the template content, so that any custom elements are upgraded using the definitions in the current document:
+
+```js
+const clone = document.importNode(template.content, true);
+// Logs "MyElement created"; my-element is upgraded using document's definitions
+document.body.appendChild(clone);
+```
+
## Specifications
{{Specifications}}
diff --git a/files/en-us/web/api/shadowroot/elementfrompoint/index.md b/files/en-us/web/api/shadowroot/elementfrompoint/index.md
index 22dbcd01387577b..e493b9e6169ce03 100644
--- a/files/en-us/web/api/shadowroot/elementfrompoint/index.md
+++ b/files/en-us/web/api/shadowroot/elementfrompoint/index.md
@@ -41,12 +41,9 @@ customElements.define(
class extends HTMLElement {
constructor() {
super();
- const templateContent = document.getElementById(
- "my-custom-element-template",
- ).content;
+ const template = document.getElementById("my-custom-element-template");
const sRoot = this.attachShadow({ mode: "open" });
- sRoot.appendChild(templateContent.cloneNode(true));
-
+ sRoot.appendChild(document.importNode(template.content, true));
// get the topmost element in the top left corner of the viewport
const srElement = this.shadowRoot.elementFromPoint(0, 0);
// apply a border to that element
diff --git a/files/en-us/web/api/web_components/using_templates_and_slots/index.md b/files/en-us/web/api/web_components/using_templates_and_slots/index.md
index 9262426f6483ed6..6f39ac954c8960e 100644
--- a/files/en-us/web/api/web_components/using_templates_and_slots/index.md
+++ b/files/en-us/web/api/web_components/using_templates_and_slots/index.md
@@ -48,13 +48,13 @@ customElements.define(
let templateContent = template.content;
const shadowRoot = this.attachShadow({ mode: "open" });
- shadowRoot.appendChild(templateContent.cloneNode(true));
+ shadowRoot.appendChild(document.importNode(templateContent, true));
}
},
);
```
-The key point to note here is that we append a clone of the template content to the shadow root, created using the {{domxref("Node.cloneNode()")}} method.
+The key point to note here is that we append a clone of the template content to the shadow root, created using the {{domxref("Document.importNode()")}} method.
And because we are appending its contents to a shadow DOM, we can include some styling information inside the template in a {{htmlelement("style")}} element, which is then encapsulated inside the custom element.
This wouldn't work if we just appended it to the standard DOM.
@@ -255,7 +255,7 @@ customElements.define(
"element-details-template",
).content;
const shadowRoot = this.attachShadow({ mode: "open" });
- shadowRoot.appendChild(template.cloneNode(true));
+ shadowRoot.appendChild(document.importNode(template, true));
}
},
);
diff --git a/files/en-us/web/api/webgl_api/tutorial/using_textures_in_webgl/index.md b/files/en-us/web/api/webgl_api/tutorial/using_textures_in_webgl/index.md
index 03fb9147be56b8a..4497e8b412db306 100644
--- a/files/en-us/web/api/webgl_api/tutorial/using_textures_in_webgl/index.md
+++ b/files/en-us/web/api/webgl_api/tutorial/using_textures_in_webgl/index.md
@@ -345,7 +345,7 @@ At this point, the rotating cube should be good to go.
Loading of WebGL textures is subject to cross-domain access controls. In order for your content to load a texture from another domain, CORS approval needs to be obtained. See [HTTP access control](/en-US/docs/Web/HTTP/Guides/CORS) for details on CORS.
-Because WebGL now requires textures to be loaded from secure contexts, you can't use textures loaded from `file:///` URLs in WebGL. That means that you'll need a secure web server to test and deploy your code. For local testing, see our guide [How do you set up a local testing server?](/en-US/docs/Learn_web_development/Howto/Tools_and_setup/set_up_a_local_testing_server) for help.
+Modern browsers usually treat the origin of files loaded using the file:/// scheme as _opaque origins_. Even when a file includes other files from the same folder, they are not assumed to come from the same origin, and may trigger CORS errors (see [Same-origin policy#File origins](/en-US/docs/Web/Security/Defenses/Same-origin_policy#file_origins)). That means that you can't use textures loaded from `file:///` URLs in WebGL and need a web server to test and deploy your code. For local testing, see our guide [How do you set up a local testing server?](/en-US/docs/Learn_web_development/Howto/Tools_and_setup/set_up_a_local_testing_server) for help.
See this [hacks.mozilla.org article](https://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/) for an explanation of how to use CORS-approved images as WebGL textures.
diff --git a/files/en-us/web/api/window/customelements/index.md b/files/en-us/web/api/window/customelements/index.md
index ee995a2aee8d57b..d761a15205bed3c 100644
--- a/files/en-us/web/api/window/customelements/index.md
+++ b/files/en-us/web/api/window/customelements/index.md
@@ -27,11 +27,9 @@ customElements.define(
class extends HTMLElement {
constructor() {
super();
- const template = document.getElementById(
- "element-details-template",
- ).content;
+ const template = document.getElementById("element-details-template");
const shadowRoot = this.attachShadow({ mode: "open" }).appendChild(
- template.cloneNode(true),
+ document.importNode(template.content, true),
);
}
},
diff --git a/files/en-us/web/api/writablestream/abort/index.md b/files/en-us/web/api/writablestream/abort/index.md
index 18eba18903eff4c..056d4b0db3cb7dd 100644
--- a/files/en-us/web/api/writablestream/abort/index.md
+++ b/files/en-us/web/api/writablestream/abort/index.md
@@ -23,7 +23,7 @@ abort(reason)
### Return value
-A {{jsxref("Promise")}}, which fulfills with the value given in the `reason` parameter.
+A {{jsxref("Promise")}}, which fulfills with `undefined`.
### Exceptions
diff --git a/files/en-us/web/css/reference/selectors/_doublecolon_slotted/index.md b/files/en-us/web/css/reference/selectors/_doublecolon_slotted/index.md
index 0c90b84a2484a68..43332fad6ac15bc 100644
--- a/files/en-us/web/css/reference/selectors/_doublecolon_slotted/index.md
+++ b/files/en-us/web/css/reference/selectors/_doublecolon_slotted/index.md
@@ -47,7 +47,7 @@ customElements.define(
const template = document.getElementById("card-template");
const shadow = this.attachShadow({ mode: "open" });
- shadow.appendChild(template.content.cloneNode(true));
+ shadow.appendChild(document.importNode(template.content, true));
const elementStyle = document.createElement("style");
elementStyle.textContent = `
@@ -117,12 +117,11 @@ customElements.define(
class extends HTMLElement {
constructor() {
super();
- let template = document.getElementById("person-template");
- let templateContent = template.content;
+ const template = document.getElementById("person-template");
const shadowRoot = this.attachShadow({ mode: "open" });
- let style = document.createElement("style");
+ const style = document.createElement("style");
style.textContent =
"div { padding: 10px; border: 1px solid gray; width: 200px; margin: 10px; }" +
"h2 { margin: 0 0 10px; }" +
@@ -132,7 +131,7 @@ customElements.define(
"::slotted(span) {text-decoration: underline;} ";
shadowRoot.appendChild(style);
- shadowRoot.appendChild(templateContent.cloneNode(true));
+ shadowRoot.appendChild(document.importNode(template.content, true));
}
},
);
diff --git a/files/en-us/web/html/reference/elements/template/index.md b/files/en-us/web/html/reference/elements/template/index.md
index 4fb38e9e7e145bd..9dafd8ab2166835 100644
--- a/files/en-us/web/html/reference/elements/template/index.md
+++ b/files/en-us/web/html/reference/elements/template/index.md
@@ -55,10 +55,10 @@ There are two main ways to use the `` element.
By default, the element's content is not rendered.
The corresponding {{domxref("HTMLTemplateElement")}} interface includes a standard {{domxref("HTMLTemplateElement.content", "content")}} property (without an equivalent content/markup attribute). This `content` property is read-only and holds a {{domxref("DocumentFragment")}} that contains the DOM subtree represented by the template.
-This fragment can be cloned via the {{domxref("Node.cloneNode", "cloneNode")}} method and inserted into the DOM.
-Be careful when using the `content` property because the returned `DocumentFragment` can exhibit unexpected behavior.
-For more details, see the [Avoiding DocumentFragment pitfalls](#avoiding_documentfragment_pitfalls) section below.
+The {{domxref("Node.cloneNode()")}} and {{domxref("Document.importNode()")}} methods both create a copy of a node. The difference is that `importNode()` clones the node in the context of the calling document, whereas `cloneNode()` uses the document of the node being cloned. The document context determines the {{domxref("CustomElementRegistry")}} for constructing any custom elements. For this reason, use `document.importNode()` to clone the `content` fragment so that custom element descendants are constructed using the definitions in the current document, rather than the separate document that owns the template content. See the {{domxref("Node.cloneNode()")}} page's examples for more details.
+
+Note that the `DocumentFragment` container itself should not have data attached to it. See the [Data on the DocumentFragment is not cloned](#data_on_the_documentfragment_is_not_cloned) example for more details.
### Declarative Shadow DOM
@@ -109,7 +109,7 @@ if ("content" in document.createElement("template")) {
const template = document.querySelector("#productrow");
// Clone the new row and insert it into the table
- const clone = template.content.cloneNode(true);
+ const clone = document.importNode(template.content, true);
let td = clone.querySelectorAll("td");
td[0].textContent = "1235646565";
td[1].textContent = "Stuff";
@@ -117,7 +117,7 @@ if ("content" in document.createElement("template")) {
tbody.appendChild(clone);
// Clone the new row and insert it into the table
- const clone2 = template.content.cloneNode(true);
+ const clone2 = document.importNode(template.content, true);
td = clone2.querySelectorAll("td");
td[0].textContent = "0384928528";
td[1].textContent = "Acme Kidney Beans 2";
@@ -254,7 +254,7 @@ This also focuses the parent element as shown below.

-## Avoiding DocumentFragment pitfalls
+## Data on the DocumentFragment is not cloned
When a {{domxref("DocumentFragment")}} value is passed, {{domxref("Node.appendChild")}} and similar methods move only the _child nodes_ of that value into the target node. Therefore, it is usually preferable to attach event handlers to the children of a `DocumentFragment`, rather than to the `DocumentFragment` itself.
@@ -280,11 +280,11 @@ function clickHandler(event) {
event.target.append(" — Clicked this div");
}
-const firstClone = template.content.cloneNode(true);
+const firstClone = document.importNode(template.content, true);
firstClone.addEventListener("click", clickHandler);
container.appendChild(firstClone);
-const secondClone = template.content.cloneNode(true);
+const secondClone = document.importNode(template.content, true);
secondClone.children[0].addEventListener("click", clickHandler);
container.appendChild(secondClone);
```
@@ -293,7 +293,7 @@ container.appendChild(secondClone);
Since `firstClone` is a `DocumentFragment`, only its children are added to `container` when `appendChild` is called; the event handlers of `firstClone` are not copied. In contrast, because an event handler is added to the first _child node_ of `secondClone`, the event handler is copied when `appendChild` is called, and clicking on it works as one would expect.
-{{EmbedLiveSample('Avoiding_DocumentFragment_pitfall')}}
+{{EmbedLiveSample(' Data on the DocumentFragment is not cloned')}}
## Technical summary
diff --git a/files/en-us/web/html/reference/global_attributes/exportparts/index.md b/files/en-us/web/html/reference/global_attributes/exportparts/index.md
index e640e0d321935cb..20e1df8b17ddde8 100644
--- a/files/en-us/web/html/reference/global_attributes/exportparts/index.md
+++ b/files/en-us/web/html/reference/global_attributes/exportparts/index.md
@@ -71,13 +71,11 @@ customElements.define(
class extends HTMLElement {
constructor() {
super(); // Always call super first in constructor
- const cardComponent = document.getElementById(
- "card-component-template",
- ).content;
+ const template = document.getElementById("card-component-template");
const shadowRoot = this.attachShadow({
mode: "open",
});
- shadowRoot.appendChild(cardComponent.cloneNode(true));
+ shadowRoot.appendChild(document.importNode(template.content, true));
}
},
);
@@ -162,13 +160,11 @@ customElements.define(
class extends HTMLElement {
constructor() {
super(); // Always call super first in constructor
- const cardComponent = document.getElementById(
- "card-component-template",
- ).content;
+ const template = document.getElementById("card-component-template");
const shadowRoot = this.attachShadow({
mode: "open",
});
- shadowRoot.appendChild(cardComponent.cloneNode(true));
+ shadowRoot.appendChild(document.importNode(template.content, true));
}
},
);
@@ -180,11 +176,11 @@ customElements.define(
class extends HTMLElement {
constructor() {
super(); // Always call super first in constructor
- const cardWrapper = document.getElementById("card-wrapper").content;
+ const template = document.getElementById("card-wrapper");
const shadowRoot = this.attachShadow({
mode: "open",
});
- shadowRoot.appendChild(cardWrapper.cloneNode(true));
+ shadowRoot.appendChild(document.importNode(template.content, true));
}
},
);
@@ -269,13 +265,11 @@ customElements.define(
class extends HTMLElement {
constructor() {
super(); // Always call super first in constructor
- const cardComponent = document.getElementById(
- "card-component-template",
- ).content;
+ const template = document.getElementById("card-component-template");
const shadowRoot = this.attachShadow({
mode: "open",
});
- shadowRoot.appendChild(cardComponent.cloneNode(true));
+ shadowRoot.appendChild(document.importNode(template.content, true));
}
},
);
@@ -287,11 +281,11 @@ customElements.define(
class extends HTMLElement {
constructor() {
super(); // Always call super first in constructor
- const cardWrapper = document.getElementById("card-wrapper").content;
+ const template = document.getElementById("card-wrapper");
const shadowRoot = this.attachShadow({
mode: "open",
});
- shadowRoot.appendChild(cardWrapper.cloneNode(true));
+ shadowRoot.appendChild(document.importNode(template.content, true));
}
},
);
diff --git a/package-lock.json b/package-lock.json
index a520b0f1cedb28e..fbef1a9ff193598 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -47,7 +47,7 @@
"yargs": "^18.0.0"
},
"engines": {
- "node": ">=22"
+ "node": ">=24"
}
},
"node_modules/@apideck/better-ajv-errors": {
@@ -9765,9 +9765,9 @@
}
},
"node_modules/rimraf/node_modules/glob": {
- "version": "10.4.5",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
- "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
"license": "ISC",
"dependencies": {
"foreground-child": "^3.1.0",
diff --git a/package.json b/package.json
index c410476f0a3044a..7e85fb7dd2b4c70 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,7 @@
"url": "git+https://github.com/mdn/content.git"
},
"engines": {
- "node": ">=22"
+ "node": ">=24"
},
"type": "module",
"scripts": {