skip to Main Content

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:

enter image description here

Fragment with content:

enter image description here

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 adding defer. (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


  1. You could get the top level element (the <aside>) from the fragment through firstElementChild before appending it to the DOM.

    this.modalElement = modalTemplate.firstElementChild;
    document.body.appendChild(modalTemplate);
    
    Login or Signup to reply.
  2. Your issue is that the template content represents a DocumentFragment

    It is used as a lightweight version of Document that stores a segment of a document structure comprised of nodes

    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

    const template = document.getElementById("js-modalTemplate");
    
    const content = template.content.cloneNode(true).firstElementChild;
    console.log('style.display:', content.style.display);
    <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>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search