admin管理员组文章数量:1332881
I've got two Lit-element web ponents - one is units-list
, which contains many units-list-item
elements. The units-list-item
elements have two different display modes: pact 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' : 'pact-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 "pact-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 pact">
<!-- pact 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 ments are not a problem):
// This SHOULD hide the pact version of the row when the
// unit list has a "detail" class applied
:host-context(.detail-view) div.rowpact {
display: none !important;
}
// This SHOULD hide the detail version of the row when the
// unit list has a "pact" class applied
:host-context(pact-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
- When
: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? - If it's the former, will
:host-context
jump multiple shadow DOM boundaries? Say I have a custompage
element that contains a customlist
element, which itself contains many customitem
elements. If thatitem
element has a:host-context
rule, will the browser first scan up the shadow DOM of thelist
element, then, if matching nothing, scan up the shadow DOM of thepage
element, and if still matching nothing, then scan up the main document DOM to the<html>
tag?
I've got two Lit-element web ponents - one is units-list
, which contains many units-list-item
elements. The units-list-item
elements have two different display modes: pact 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' : 'pact-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 "pact-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 pact">
<!-- pact 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 ments are not a problem):
// This SHOULD hide the pact version of the row when the
// unit list has a "detail" class applied
:host-context(.detail-view) div.row.pact {
display: none !important;
}
// This SHOULD hide the detail version of the row when the
// unit list has a "pact" class applied
:host-context(.pact-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
- When
: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? - If it's the former, will
:host-context
jump multiple shadow DOM boundaries? Say I have a custompage
element that contains a customlist
element, which itself contains many customitem
elements. If thatitem
element has a:host-context
rule, will the browser first scan up the shadow DOM of thelist
element, then, if matching nothing, scan up the shadow DOM of thepage
element, and if still matching nothing, then scan up the main document DOM to the<html>
tag?
-
1
At second sight it looks like you have a typo.. :host-content Experiment in a JSFiddle. I don't have one because there is no support for
:host-context
in FireFox or Safari; last update from a month ago is both Mozilla and Apple are not going to implement it. Looks like it is going to be removed from the spec: github./w3c/csswg-drafts/issues/1914 One alternative is to use CSS Properties (those trickle down into shadowDOM) – Danny '365CSI' Engelman Commented May 1, 2020 at 13:20 - Thank you, I've fixed the typo, but it was only in the question, not in the actual code. And custom properties would be fine if I was trying to just change a color, but I'm trying to get an element to display/not display based on a parent element's class. That's not something custom properties are really suited for. – Isochronous Commented May 1, 2020 at 14:14
-
1
I did a quick JSFiddle test extending on the MDN example, works fine. Are those
!importants
your Nemesis? Usingimportant
is a sure way to get into trouble eventually; avoid them with Specificity. – Danny '365CSI' Engelman Commented May 1, 2020 at 14:32 -
No, I only added the
!important
after everything else had failed to work. The fact that your fiddle works and my code doesn't suggests to me that the issue has something to do with nested web ponents. The fact that this isn't supported by Firefox means that this is a non-starter anyway, so I'll have to turn to an alternative solution. Thanks for the help! – Isochronous Commented May 1, 2020 at 14:44 - And why are CSS properties not suited? That, and forcing with JS, is the only way to get the 'outside' into shadowDOM. You are not restricted to styling, you can stuff any string value in CSS properties. – Danny '365CSI' Engelman Commented May 1, 2020 at 15:04
2 Answers
Reset to default 6There is no support for :host-context
in FireFox or Safari
It will be removed from the spec: https://github./w3c/csswg-drafts/issues/1914
One alternative is to use CSS Properties (those trickle down into shadowDOM)
JSFiddle: https://jsfiddle/WebComponents/hpd6yvxt/
- using
host-context
for Chrome and Edge - using CSS properties for other Browsers
Update Feb 2022
https://caniuse./?search=host-context
Update Feb 2023
An example of using css porperties, as Danny Engelman says, to get your goal
customElements.define('list-item', class extends HTMLElement {
constructor() {
const style = document.createElement('style');
const divpact = document.createElement('div');
divpact.innerHTML = "pact";
divpact.className = "pact";
const divdetail = document.createElement('div');
divdetail.innerHTML = "detail";
divdetail.className = "detail";
let shadow = super().attachShadow({
mode: 'open'
});
shadow.append(style, divpact, divdetail);
style.innerHTML = `
.pact {
background-color: red;
display: var(--display-pact, block);
}
.detail {
background-color: green;
display: var(--display-detail, block);
}
`
}
});
.pact-view {
--display-detail: none;
}
.detail-view {
--display-pact: none;
}
.box {
width: 200px;
height: 50px;
border: solid 1px black;
margin: 5px;
}
<div class="box">
no class applied
<list-item>test</list-item>
</div>
<div class="pact-view box">
pact view
<list-item>test</list-item>
</div>
<div class="detail-view box">
detail view
<list-item>test</list-item>
</div>
本文标签: javascripthostcontext not working as expected in LitElement web componentStack Overflow
版权声明:本文标题:javascript - :host-context not working as expected in Lit-Element web component - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742313389a2451328.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论