QuerySelector在HTML导入时找不到模板

时间:2016-06-06 19:10:04

标签: javascript html google-chrome web-component

我目前正在尝试使用最新稳定的Chrome 52来学习如何使用Web组件(不使用Polymer)(我还尝试使用Chrome 52上的webcomponents.js polyfill)。但是,当我这样做时,我似乎得到querySelector的错误。当我尝试通过document.querySelector('#template')在控制台中获取(通常名称很差的模板ID)时,它为空并且无法找到它。

我正在使用this guide,虽然有一些ES6语法。 (我也试过直接复制和粘贴,它有同样的问题)

我也尝试在shadowDOM中搜索,但它也不存在。

view.html

<template id="template">
  <style>
  </style>
  <div class="container">
    <h1>WZView</h1>
  </div>
</template>
<script>
"use strict";

class WZView extends HTMLElement {

  createdCallback () {
    var root = this.createShadowRoot();
    var template = document.querySelector('#template');
    root.appendChild(document.importNode(template.content, true));
  }

}

document.registerElement('wz-view', WZView);
</script>

的index.html

<!DOCTYPE html>
<html>
<head>
<!--<script src="/bower_components/webcomponentsjs/webcomponents.js"></script>-->
<link rel="import" href="view.html">
</head>
<body>
  <wz-view></wz-view>
</body>
</html>

控制台:

view.html:16 Uncaught TypeError: Cannot read property 'content' of null
> document.querySelector('#template')
null

4 个答案:

答案 0 :(得分:3)

在导入的HTML中的<script>内,请勿使用document.querySelector(...)

使用:

// 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');

示例(修复问题中的示例)

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));
    }
}

<强>兼容性:

IE 11 won't support it。大多数browsers (including Edge) implement itIE 10 and below there is a polyfill

答案 1 :(得分:1)

我遇到了同样的问题,我一直在搞乱,直到我得到了一些有用的东西。

如果您使用Eespresso,则可以获得当前导入。将uiautomator添加到其中将为您提供导入的文档,然后您可以使用该文档来查询选择器

Robotium

修改

这很脆弱,为了做两种不同的进口,它有点困难。

我把它分解成了自己的功能。首先,您需要使用document.querySelector('link[rel=import]')获取页面上的所有导入。然后使用map你可以将实际的模板值插入到数组中,然后使用快速过滤器来删除空值,你可以获取第一个也是唯一的元素,这将是正确的模板。

.import

注意:

我本可以使用filter作为唯一的数组操作,代替map,但这只会给我一个包含链接的数组,所以我必须要有另一个变量才能在该过滤器中捕获它操作,或再次运行var template = document.querySelector('link[rel=import]').import.querySelector('#template');

答案 2 :(得分:1)

更新:我原来的答案是垃圾。我的问题是我试图从类里面的中获取currentScript.ownerDocument,而不是在当前文档中主动运行的脚本中获取class Foo extends HTMLElement { constructor() { const template = document.currentScript.ownerDocument.querySelector("template"); // do something with `template` } } (例如,在我定义类,因此,脚本将与模板一起运行的位置)。可以从另一个脚本调用方法,&#34; currentScript&#34;那时(也就是说,可能是一个完全不同的文件,特别是如果你从其他进口商品中进口,就像我一样)。

所以这很糟糕:

(() => {

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

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

})();

这更好:

currentScript

希望能帮助像我这样愚蠢的其他人。

原始回答:

我在尝试从某种深度的导入层次结构访问模板时遇到了问题。在这种情况下,currentScript建议对我不起作用:在Chrome / Chromium中,currentScript始终引用第一个导入,但从不涉及任何更深层次的导入(如我在上面提到的那样)评论@ acdcjunior的回答),在Firefox(通过polyfill)中,null 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); }

所以我最终做的事情与@Caranicas的答案类似。我创建了一个实用程序函数,它找到导入的文件,在IIFE中调用它一次,然后使它成为类的属性,如下所示:

的index.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 / app.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>

的src / component.html:

_resolveImport

deltaT['data'] = (deltaT['hws'] - deltaT['hwr']).apply(lambda x: max(x, 0)) 价格昂贵,因此最好不要为每次导入多次调用此内容,并且仅针对实际需要它的导入。

答案 3 :(得分:0)

使用多填充HTML导入(npm @ webcomponents / html-imports ^ 1.2),组件<template>结束放置在主文档标题中的某个位置。使用本机HTML导入,最终将其放置在单独的文档中。在这两种情况下找到模板的可靠方法是:

[my-component.html]
<template id="my-component">
  ...

<script>
  ...
  const script = document.currentScript;
  const template = script.ownerDocument.querySelector('template#my-component');
  ...
  customElements.define('my-component', ...);

为每个模板分配一个唯一的ID(例如组件名称),以便在多填充情况下选择正确的模板(guide可能在这方面有点太简单了)