How do I create a modifiable "template" document in javascript?

If I have html like this:

// more html
<div id="myTemplate">
	// more html
	<form id="form">
	<input type="hidden" name="your_name_here" value="">
    </form>
	// more html
</div>

// more html

I want to create a “mini” document that I can use the general document functions on, like getElementById() and getElementsByName().

But if I use

let myClone = document.getElementById('myTemplate').cloneNode(true);

I cannot manipulate the myClone variable with functions like getElementById and getElementsByName, because I receive TypeError: myClone.getElementById is not a function

I especially want to select all input elements by their name (here it is “your_name_here”) and set their values in this html selection.

What is the proper way to do this? I want to manipulate the html and then paste it back on the page in a different spot. I will have to do this repeatedly, with different values each time.

Here’s a related question I had while trying to solve this myself. The function querySelector() works with myClone: myClone.querySelector(). Why does this function work but functions like getElementsByName() do not? How do I know where and what objects querySelector() works on but functions like getElementsByName() do not?

That isn’t a clone of the template. Instead it’s a reference directly to the template itself.

To create a clone, you’ll want to use cloneNode along with the true argument, telling to to make a deep clone.:

let myClone = document.getElementById('myTemplate').cloneNode(true);

The following page gives good details about using JavaScript to manipulate HTML templates.

Sorry, I just forgot to add that in my simple code above! I do use cloneNode() in my real code, like this:

let myClone = document.getElementById('myTemplate').cloneNode(true);

And it still does not work. Thanks for pointing that out, though. I edited it above.

I actually already read that link (and based my code around it) but cannot use html templates in this situation. The link also only uses querySelector on the clone, not the functions I am trying to use like getElementsByName().

I think that the main reason why getElementById and getElementsByName don’t work for you is that those are restricted to working only on the document, and not on subsets of it.

I am definitely open to achieving the same result through alternatives means, but I’d prefer something that lets me manipulate HTML using the document functions since there are so many tutorials on how to do it available. (I guess that it would be faster to manipulate a subset of the dom, rather than the whole dom, too.)

Can I create a document object out of existing HTML? Examples I have found seem to show only adding one element at a time.

Sorry no. There is only one document element.

I guess that I need to make this template in some other way then. Can anyone suggest good ways to make a mini template, and then also show how to select all form inputs by name and then set their values within the template?

Hi @Torite, you can still get elements by name using querySelector()… e.g.

<template id="my-template">
  <form>
    <input type="hidden" name="timestamp">
  </form>
</template>
const template = document.getElementById('my-template')
const content = template.content.cloneNode(true)

content.querySelector('[name="timestamp"]').value = Date.now()
document.body.appendChild(content)

Or using the form’s elements collection:

const template = document.getElementById('my-template')
const content = template.content.cloneNode(true)
const form = content.querySelector('form')

form.elements.namedItem('timestamp').value = Date.now()
document.body.appendChild(content)

BTW be careful with IDs inside templates; if you want to render its content more than once, be sure to append an increment ID or something to avoid duplicate IDs in your document… for instance, if you need IDs for input / label relations:

<template id="my-template">
  <form>
    <input type="hidden" name="timestamp">
    <label for="username">Username</label>
    <input type="text" id="username" name="username">
  </form>
</template>
;['John', 'Paul', 'Ringo', 'George'].forEach((name, index) => {
  const content = template.content.cloneNode(true)
  const form = content.querySelector('form')

  form.elements.namedItem('timestamp').value = Date.now()
  form.elements.namedItem('username').value = name

  content.querySelectorAll('[id]').forEach(element => {
    const { id } = element
    element.id = id + '-' + index

    content.querySelectorAll(`[for="${id}"]`).forEach(label => {
      label.setAttribute('for', element.id)
    })
  })

  document.body.append(content)
})
3 Likes

Thanks for the very informative code! And especially for the way to select all form elements by name and the note about IDs. (That is a problem I would have run into immediately.) This helps a lot.

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.