30

I have two similar selections. The first uses a <div> tag, which works fine, the second uses a newly <template> tag, which doesn't work anymore.

Can anyone tell me how to get this to work with jQuery using the <template> tag?

HTML

<div id="div">
    <div>content</div>
</div>

<template id="template">
    <div>content</div>
</template>

JavaScript

var $div = $('#div');
var $content = $div.find('div');
console.log($content); //works ($content.length == 1)

var $template = $('#template');
var $content = $template.find('div');
console.log($content); //doesn't work ($content.length == 0)

http://jsfiddle.net/s8b5w0Le/1/

4
  • I'm using Chrome 39.0.2171.71 m (in case it matters) Commented Dec 5, 2014 at 7:57
  • $template[0].outerHTML works, so it should theoretically be possible for jQuery to work with the <template> tag Commented Dec 5, 2014 at 8:07
  • So, you cannot use jQuery alone for this work, but you can check if the content attribute exists? If so, use it - if not, the regular jQuery way should work? Commented Dec 5, 2014 at 8:22
  • 1
    try looking here stackoverflow.com/questions/15930706/…
    – Stefan
    Commented Dec 5, 2014 at 8:24

8 Answers 8

37

HTMLTemplateElement saves the DOM into a separate attribute.

jQuery

<script src="jquery-3.1.0.js"></script>
<script type="text/javascript">
    $(document).ready(function()
    {
        var $div = $('#div');
        var $content = $div.find('div');
        console.log($content.text()); // output "content", inner div

        var $template = $('#template');
        var node = $template.prop('content');
        var $content = $(node).find('div');
        console.log($content.text()); // output "content", inner template
    });

JavaScript

document.createElement('template').content
6

I'm fairly certain this has to do with Chrome's use of shadow dom (thank Polymer... )

You can either try your luck using the /deep/ combinator (probably won't work on other browsers), but I think the most robust solution would be $template[0].outerHTML as in your comment if you just need the text.

If you need jQuery functionality, using $.parseXML (to avoid Chrome's native dom construction) would probably do the trick across all browsers (can confirm Chrome + FF).

Example here: http://jsfiddle.net/3fe9jjfj

var tc = $('#template')[0].outerHTML;

$template = $($.parseXML(tc)).contents();

console.log($template);
console.log($template.find('div'));

Both logs return as we'd expect, and $template can now be treated as an ordinary jQuery object.

3
  • Great, thanks! Sometimes, the best answers come from users with the lowest amount of reputation score ;) Commented Dec 5, 2014 at 10:30
  • There is a problem, however, if you have non XML compatible tags (i.e. <hr>), you would need to write <hr/>, but outerHTML still returns <hr> - so this might be tricky Commented Dec 5, 2014 at 10:41
  • @SimonFerndriger Depending on what your use of template entails, you may be able to alternately get away with using a custom tag name (e.g. xtemplate), rather than template. If you're using your template to bind data with Handlebars or something, then that should work. Again, hacky, but I guess this is what is expected with early adoption Commented Dec 5, 2014 at 15:03
2

As others have noted, Chrome puts <template> child elements into a shadow DOM. To access them:

// Access the JavaScript object for the template content
$('template')[0]

// Make a jQuery selection out of it
$($('template')[0])

// Now you can search it
$($('template')[0]).find('div.someclass').css('color','#000');
2

A way, too late for the party but I ended up doing this:

function resolveTemplate(id) {
  return $(id).contents();
}

...

var $searchIcon = resolveTemplate('#search-icon-template');
$('#div').append($searchIcon);
0

You can use all the JQuery methods as usual if the element inside the template element are wrapped with a container.

const temp = $("#template").contents().clone();
$(temp).find("h1").text("A dynamic title");
temp.appendTo($("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div id="app"></div>

<template id="template">
    <div class="container">
        <h1>lorem ipsum</h1>
        <p>lorem ipsum </p>
        <img src="" alt="">
    </div>
</template>

The container can also be appended dynamically with JQuery. Or if you don't want a container, you can append its content.

const temp = $('<div></div>').html($("#template").contents().clone());
$(temp).find("h1").text('dynamic title');
$(temp).find("p").text('But no container this time');
temp.contents().appendTo($("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div id="app"></div>

<template id="template">
    <h1>lorem ipsum</h1>
    <p>lorem ipsum </p>
    <img src="" alt="">
</template>

-1
<template>
   <div class="template-container">
      <div class="content">content</div>
   </div>
</template>
var templateHtml = ('#template').html() // this will return the template container div
var template = $(templateHtml);
var content = template.find('.content');

console.log(content);
3
  • 2
    Please add some explanation to your code. Commented Sep 23, 2021 at 8:41
  • This only creates a new element with the template code. It doesn't select the actual template content itself. It's a copy essentially. This would not be be helpful for things like manipulating the DOM in the template element. Commented Nov 23, 2021 at 7:05
  • your code needs some fixing to get it to work. Please check your code and make sure it works before posting an answer.
    – Aurovrata
    Commented Dec 6, 2022 at 11:33
-3

var $content = $template.content.find('div');

... instead of ...

var $content = $template.find('div');

Worked for me.

3
  • OK, and what about Internet Explorer? Commented Dec 5, 2014 at 8:09
  • You probably mean $template[0].content (from the HTMLObject, not from the jQuery object) - but this doesn't work in Internet Explorer as far as I know: developer.mozilla.org/en-US/docs/Web/HTML/Element/template Commented Dec 5, 2014 at 8:13
  • I did console.log($template) to look at the structure of the template-Tag. In Chrome and Firefox it seems to be different.
    – what
    Commented Dec 5, 2014 at 8:17
-3

HTML5 template is display: none; by default, childNodes in template is invalid, if you inspect it in console you'll find something different

Not the answer you're looking for? Browse other questions tagged or ask your own question.