I’m pulling my hair out on this one. I have a JavaScript class that clones a <template>
element and then is appended to the document body. I want to then manipulate that newly created element… but it always insists it’s undefined.
Template
<template id="js-modalTemplate">
<aside class="o--modal" style="display: none">
<button class="o-modal__close-button">
Close
</button>
<div><iframe class="js-video" src=""></iframe></div>
</aside>
</template>
ES Module
export default class Modal {
constructor() {
const template = document.getElementById('js-modalTemplate');
const modalTemplate = template.content.cloneNode(true)
this.modalElement = document.body.appendChild(modalTemplate)
console.log(this.modalElement)
this.modalElement.style.display = "block"
}
}
Script element
<script type="module">
import VideoModal from "{{ Vite::asset('resources/js/components/Modal.js') }}"
window.testModal = new Modal()
</script>
The console.log output of the element sometimes appears in full, but sometimes it’s empty (no changes to code)…
Empty fragment:
Fragment with content:
However I always get the following error, no matter what:
Uncaught TypeError: Cannot set properties of undefined (setting ‘display’)
And the element always appears in the DOM at the expected place.
Things I’ve tried
- Moving the script element to the bottom of
document.body
and addingdefer
. (Neither of these things should be necessary because it’s an ES Module anyway and so it automatically deferred.) - Placing the code within
window.onload
for similar reasons
There’s just something about using a <template>
that causes this issue, but I’ve never used them before so I don’t know what!
2
Answers
You could get the top level element (the
<aside>
) from the fragment throughfirstElementChild
before appending it to the DOM.Your issue is that the template content represents a DocumentFragment…
That is, it is a container for the content, not the content itself.
If you want to operate on any particular node like the
<aside>
, you need to locate it within the fragment.For example, using the firstElementChild property