Home > OS >  document.getElementById not behaving as expected
document.getElementById not behaving as expected

Time:06-10

I'm trying to get the contents of a div with a specific Id. However, if the div is nested inside a tag such as <template> it can't be found. I don't have control over the html output of the page, so I need to find a way to find this element no matter where its nested. It's my understanding that document.getElementById is supposed to be able to find an element with the given Id no matter where it is located. The javascript code is always located in script tags at the end of the body.

Below it is working as expected, nested within a div.

const str = document.getElementById("somethingsomething").innerText;
console.log(str);
<html>
<body>
<div>
  <div hidden="" id="somethingsomething">
    <b>dark side</b>
  </div>
</div>
</body>
</html>

Here is the example that I would expect to work in the same manner but doesn't.

const str = document.getElementById("somethingsomething").innerText;
console.log(str);
<html>
<body>
  <template>
  <div hidden="" id="somethingsomething">
    <b>dark side</b>
  </div>
</template>
</body>
</html>

CodePudding user response:

A <template>'s children don't get rendered - they aren't really on the page. As MDN says:

Think of a template as a content fragment that is being stored for subsequent use in the document. While the parser does process the contents of the <template> element while loading the page, it does so only to ensure that those contents are valid; the element's contents are not rendered, however.

It doesn't have children in the DOM.

console.log(document.querySelector('template').children.length);
<template>
  <p>foo</p>
</template>

So, any descendant of a template won't be found with document.querySelector or any of the other methods to find elements that start with the document as the root.

I think you'll need to do two things here:

  • First check if the element exists on the page
  • If it doesn't, it may be in a template; iterate over the templates and their .content to see if what you're looking for is inside there

const getText = doc => doc.getElementById("somethingsomething")?.textContent;
const getStr = () => {
  const textOnPage = getText(document);
  if (textOnPage !== undefined) return textOnPage;
  for (const template of document.querySelectorAll('template')) {
    const textInTemplate = getText(template.content);
    if (textInTemplate !== undefined) return textInTemplate;
  }
};
const str = getStr();
console.log(str);
<html>
<body>
  <template>
  <div hidden="" id="somethingsomething">
    <b>dark side</b>
  </div>
</template>
</body>
</html>

CodePudding user response:

A <template> element contains markup, but the contents are not part of the DOM. The content can be added to the DOM, one or more times, by JavaScript after the page has loaded.

CodePudding user response:

The <template> tag keeps all content within it hidden from being rendered until it is actually needed. So you need to activate the template to get any elements defined within it otherwise they will return null.

You can do that by using document.importNode(externalNode, deep). Where externalNode references the template element itself and deep indicates if you want the entire subtree of the template being imported.

Once you've imported node then you need to append it to the document body for it to be rendered onto the page so getElementById can see it.

You can find more in depth examples in the following links: document.getElementById doesn't work with <template> custom tag https://www.html5rocks.com/en/tutorials/webcomponents/template/#toc-using https://developer.mozilla.org/en-US/docs/Web/API/Document/importNode

  • Related