Depending on the content of a <template>
, I want to wrap its contents in a container for easier/consistent traversal. If the contents are <style>
and <one-other-element>
at the top level, I'll leave it be. Otherwise, whatever's in there will get wrapped in a <div>
.
Originally I made my code something like this:
var hasCtnr = template.content.querySelector(':scope > :only-child, :scope > style:first-child + :last-child') != null;
But, I noticed it wasn't working -- that is, hasCtnr
was always false
. So, I made a reduced test case (jsfiddle). As you can see, :scope
works with regular DOM elements. However, it doesn't seem to work with DocumentFragment
s. I know the technology is new/experimental but is this a bug or am I doing something wrong?
If I use jQuery, it works... but my guess is because jQuery is doing something manually.
var hasCtnr = !!$(template.content).children(':only-child, style:first-child + :last-child').length;
I only care about Chrome/Electron support, by the way.
Here's the jsfiddle inline:
var nonTmplResult = document.querySelector('#non-template-result');
var tmplResult = document.querySelector('#template-result');
var grandparent = document.querySelector('#grandparent');
var parent = document.querySelector('#parent');
var child = document.querySelector('#child');
var who = grandparent.querySelector(':scope > div');
if (who === parent) {
nonTmplResult.innerHTML = 'parent as expected, :scope worked';
} else if (who === child) {
nonTmplResult.innerHTML = "child (unexpected), :scope didn't work";
}
var tmpl = document.querySelector('template');
var content = tmpl.content;
var proto = Object.create(HTMLElement.prototype);
var hasCtnr = content.querySelector(':scope > div'); // this and even ':scope div' results in null, 'div' results in DIV
tmplResult.innerHTML += hasCtnr == null ? "null for some reason, :scope didn't work" : hasCtnr.nodeName + ', :scope worked'; // Why is this null..?
tmplResult.innerHTML += '<br/>';
proto.createdCallback = function() {
var clone = document.importNode(content, true);
var root = this.createShadowRoot();
root.appendChild(clone);
var rootHasCtnr = root.querySelector(':scope > div'); // ':host > div' seems to work but I prefer this check to happen once (above) so createdCallback can be efficient as I'll likely have many custom elements
tmplResult.innerHTML += rootHasCtnr == null ? "null again, :scope didn't work" : rootHasCtnr.nodeName + ', :scope worked'; // Why is this also null..?
};
document.registerElement('x-foo', { prototype: proto });
#non-template-result {
background: red;
color: white;
}
#template-result {
background: green;
color: springgreen;
}
* /deep/ * {
margin: 10px;
padding: 5px;
}
#grandparent {
display: none;
}
<div id="grandparent">
<div id="parent">
<div id="child"></div>
</div>
</div>
<div id="non-template-result">????</div>
<div id="template-result"></div>
<x-foo>
<p>I should be dark golden rod with khaki text.</p>
</x-foo>
<template>
<style>
:host {
background: blue;
display: block;
}
:host > div > p {
color: white;
}
::content > p {
background: darkgoldenrod;
color: khaki;
}
</style>
<div>
<p>I should be blue with white text</p>
<content></content>
</div>
</template>
<a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components#Enabling_Web_Components_in_Firefox">Enabling Web Components in Firefox</a>