I've got two Lit-element web components - one is units-list
, which contains many units-list-item
elements. The units-list-item
elements have two different display modes: compact and detailed. Because the list element supports infinite scroll (and thus could contain several thousand units), we need any mechanism that toggles between the two modes to be as performant as possible.
That's why I thought an ideal solution would be to use the :host-context()
pseudo-selector in the styles for the units-list-item
element, as that way every units-list-item
element could switch between the two display modes just by changing the class applied to an ancestor (which would be within the shadow DOM of the units-list
element).
To elaborate, here's the relevant markup from the units-list
element. Note that the "trigger" classes are being applied to the #list-contents
div, which is part of the units-list
template.
<div id="list-contents" class="${showDetails ? 'detail-view table' : 'compact-view table'}">
${units.map(unit => html`<units-list-item .unit="${unit}"></units-list-item>`)}
</div>
As you can see, the showDetails
flag controls whether the "detail-view" or "compact-view" class is applied to the div containing all of the units-list-item
elements. Those classes are definitely being applied correctly.
Here's the full render method from the units-list-item
element (unnecessary markup removed):
render() {
const {unit} = this;
// the style token below injects the processed stylesheet contents into the template
return html`
${style}
<div class="row compact">
<!-- compact row markup here -->
</div>
<div class="row detail">
<!-- detail row markup here -->
</div>
`;
}
Then I have the following in the units-list-item
element's styles (we're using SCSS, so the single-line comments are not a problem):
// This SHOULD hide the compact version of the row when the
// unit list has a "detail" class applied
:host-context(.detail-view) div.row.compact {
display: none !important;
}
// This SHOULD hide the detail version of the row when the
// unit list has a "compact" class applied
:host-context(.compact-view) div.row.detail {
display: none !important;
}
My understanding of the :host-context selector says that this should work, but Chrome just renders both versions of the row every time, and the Chrome dev tools show that the selectors are never matching with either of the rows.
I know there are several alternatives that would work, but this is the only one I'm aware of that would allow the entire list of units to switch modes by changing a single class on a parent element. Every other solution I've considered would require, at the least, updating the class attribute on every units-list-item
element in the list. I'd like to avoid that if possible.
Of course, my primary concern is simply to make this work, if possible, but I'm also curious about a couple of things and can't find any info about them. The two questions I can't seem to find an answer for are
:host-context
is used within an element that is itself part of a shadow DOM, does it consider that parent element's shadow DOM to be the "host context", or does it jump "all the way out" to the document DOM?:host-context
jump multiple shadow DOM boundaries? Say I have a custom page
element that contains a custom list
element, which itself contains many custom item
elements. If that item
element has a :host-context
rule, will the browser first scan up the shadow DOM of the list
element, then, if matching nothing, scan up the shadow DOM of the page
element, and if still matching nothing, then scan up the main document DOM to the <html>
tag?There is no support for :host-context
in FireFox or Safari
It will be removed from the spec: https://github.com/w3c/csswg-drafts/issues/1914
One alternative is to use CSS Properties (those trickle down into shadowDOM)
host-context
for Chrome and Edgehttps://caniuse.com/?search=host-context