QuerySelector not finding template on HTML import

In the <script>s inside the imported HTML, don't use document.querySelector(...).

Use:

// while inside the imported HTML, `currentDocument` should be used instead of `document`
var currentDocument = document.currentScript.ownerDocument;
...
// notice the usage of `currentDocument`
var templateInsideImportedHtml = currentDocument.querySelector('#template');

Example (fixing the example in the question):

var currentDocument = document.currentScript.ownerDocument; // <-- added this line

class WZView extends HTMLElement {
    createdCallback () {
        var root = this.createShadowRoot();
        var template = currentDocument.querySelector('#template'); // <-- changed this line
        root.appendChild(document.importNode(template.content, true));
    }
}

Compatibility:

Only IE 11 won't support it. Most browsers (including Edge) implement it, and for IE 10 and below there is a polyfill.


Update: My original answer is garbage. My problem was that I was trying to obtain the currentScript.ownerDocument from a method inside the class, instead of in a script actively running in the current document (e.g., in an IIFE where I define the class and, hence, where the script would be running alongside of the template). A method may be called from another script, the "currentScript" at that point (i.e., possibly a different document altogether, especially if you're importing from other imports, like I was).

So this is bad:

class Foo extends HTMLElement {
    constructor() {
        const template = document.currentScript.ownerDocument.querySelector("template");
        // do something with `template`
    }

}

and this is better:

(() => {

const _template = document.currentScript.ownerDocument.querySelector("template");

class Foo extends HTMLElement {
    constructor() {
        // do something with `_template`
    }
}

})();

Hopefully that helps someone else who is dumb like me.


Original answer:

I also encountered problems trying to gain access to templates from an import hierarchy of some depth. The currentScript suggestion didn't work for me in this case: in Chrome/Chromium, the currentScript always referred to the first import, but never to any of the deeper imports (as I mentioned in a comment to @acdcjunior's answer), and in Firefox (via polyfill), the currentScript was null.

So what I ended up doing was something similar to @Caranicas's answer. I created a utility function that finds the imported file, call it once outside of the class in an IIFE, and then made it a property of the class, like this:

index.html:

    var _resolveImport = function(file) {
        return (function recur(doc) {
            const imports = doc.querySelectorAll(`link[rel="import"]`);
            return Array.prototype.reduce.call(imports, function(p, c) {
                return p || (
                    ~c.href.indexOf(file)
                        ? c.import
                        : recur(c.import)
                );
            }, null);
        })(document);
    }

src/app.html:

<link rel="import" href="src/component.html">
<template>...</template>
<script>
((global) => {

    const _import = global._resolveImport("src/app.html");

    class App extends HTMLElement {

        static get import() {
            return _import;
        }

        connectedCallback() {
            this.render();
            this.$component = new global.Component();
        }

        render() {
            let template = this.constructor.import.querySelector("template");
            //...
        }

        //...
    }
})(this);
</script>

src/component.html:

<template>...</template>
<script>
((global) => {

    const _import = _resolveImport("src/component.html");

    class Component extends HTMLElement {

        static get import() {
            return _import;
        }

        render() {
             let template = this.constructor.import.querySelector("template");
             //...
        }

        //...
    }
    global.Component = Component;
})(this);
</script>

_resolveImport is expensive, so it's a good idea not to call this more than once for each import, and only for imports that actually need it.