javascript - 如何将纯 CSS 转换为 Web 组件并正确应用子选择器?
问题描述
我在https://codepen.io/veksi/pen/xxGpXWM?editors=1010的 Pen 中也有代码,但在此处复制以供后代使用。
我有一段代码可以在不使用 Web 组件的情况下使用 CSS(在这种情况下是 lit-html,但没关系)。但是,由于我努力将“纯 CSS”版本转换为与 Web 组件一起使用的版本,因此存在某种心理障碍。我认为问题在于:host
和::slotted
选择器以及使用它们,但它让我困惑到底是什么问题。有人可以帮助并用正确的选择器指路吗?
这是代码:
<html>
<head>
<style>
* {
margin: 0;
padding: 0;
}
.sidebar-test {
overflow: hidden;
}
.sidebar-test > * {
display: flex;
flex-wrap: wrap;
margin: calc(var(10px) / 2 * -1);
}
.sidebar-test > * > * {
margin: calc(var(10px) / 2);
flex-basis: 1;
flex-grow: 1;
}
.sidebar-test > * > :first-child {
flex-basis: 0;
flex-grow: 999;
min-width: calc(50% - 10px);
}
</style>
</head>
<body style="width: 95vw; border-style: solid;">
<div style="width: 80vw; height:40vh; border-style: dotted;">
<p>Testing sidebar without web compoment</p>
<div class="sidebar-test">
<form>
<input type="text" aria-label="Search" />
<button type="submit">Some longer search text to realign on small views</button>
</form>
</div>
<div class="sidebar-test">
<div>
<p style="background-color: yellow">Strawberries.</p>
<img src="https://images.unsplash.com/photo-1495570689269-d883b1224443" width="100" height="100">
</div>
</div>
</div>
<div style="width: 80vw; height:40vh; border-style: dotted;">
<p>Testing sidebar1...</p>
<test-sidebar1 space="10px" side="right" contentMin="55.0%" style="border-style: dashed;">
<form>
<input type="text" aria-label="Search" />
<button type="submit">Some longer search text to realign on small views</button>
</form>
</test-sidebar1>
<test-sidebar1 space="10px" side="left" contentMin="55.0%" style="border-style: dashed;">
<div>
<p style="background-color: yellow">Strawberries.</p>
<img src="https://images.unsplash.com/photo-1495570689269-d883b1224443" width="100" height="100">
</div>
</test-sidebar1>
</div>
<div style="width: 80vw; height:40vh; border-style: dotted;margin-top:2rem;">
<p>Testing sidebar2...</p>
<test-sidebar2 space="10px" side="right" contentMin="55.0%" style="border-style: dashed;">
<form>
<input type="text" aria-label="Search" />
<button type="submit">Some longer search text to realign on small views</button>
</form>
</test-sidebar2>
<test-sidebar2 space="10px" side="left" contentMin="55.0%" style="border-style: dashed;">
<div>
<p style="background-color: yellow">Strawberries.</p>
<img src="https://images.unsplash.com/photo-1495570689269-d883b1224443" width="100" height="100">
</div>
</test-sidebar2>
</div>
</body>
</html>
import { LitElement, css, html } from 'https://unpkg.com/lit-element?module';
/* This seem to work almost while version 2 does not at all.
The problem in this version is alignment. The 'Search' button
is not on the right side and the search field occupying the
free space accordingly. For some reason flex-basis and
flex-grow are not matched by the CSS selector...
Basically trying to recreate .sidebar-test as given in the document CSS, but wrapped into an element.
*/
export class Sidebar1 extends LitElement {
static get styles() {
return css`
:host {
overflow: hidden;
}
:host ::slotted(*) {
display: flex;
flex-wrap: wrap;
}
:host ::slotted(*) > ::slotted(*) {
flex-grow: 1;
}
`;
}
get sideStyle() {
return html`
<style>
:host ::slotted(*) { margin: calc(${this.space} / 2 * -1); ${this.noStretch ? 'align-items: flex-start;' : ''} }
:host ::slotted(*) > ::slotted(*) { margin: calc(${this.space} / 2); ${this.sideWidth ? `flex-basis: ${this.sideWidth};` : ''} }
:host ::slotted(*) > ${this.side !== 'left' ? `::slotted(*:first-child)` : `::slotted(*:last-child)`} {
flex-basis: 0;
flex-grow: 999;
min-width: calc(${this.contentMin} - ${this.space});
}
</style>
`;
}
static get properties() {
return {
side: { type: String },
sideWidth: { type: String },
contentMin: { type: String },
space: { type: String },
x: { type: String },
noStretch: { type: Boolean, reflect: true, attribute: true }
};
}
constructor() {
super();
this.side = "left";
this.contentMin = "50%";
if (this.children[0].children.length != 2) {
console.error(`Should have exactly two children..`);
}
}
render() {
if (!this.contentMin.includes('%')) {
console.warn('Minimum content should be in percentages to avoid overflows.');
}
return html`${this.sideStyle}<slot></slot>`;
}
}
customElements.define('test-sidebar1', Sidebar1);
/* Compared to Sidebar1 there's a problem with layout. As if sideStyle wouldn't be applied correctly.*/
export class Sidebar2 extends LitElement {
static get styles() {
return css`
:host {
--sidebarSpace: 1rem;
--sidebarContentMin: 50%;
--sidebarWidth: 30ch;
overflow: hidden;
}
:host > ::slotted(*) {
display: flex;
flex-wrap: wrap;
margin: calc(var(--sidebarSpace) / 2 * -1);
}
:host([noStretch]) {
:host > ::slotted(*) {
align-items: flex-start;
}
}
:host > ::slotted(*) > ::slotted(*) {
margin: calc(var(--sidebarSpace) / 2);
flex-grow: 1;
}
:host > ::slotted(*) > ::slotted(*:first-child) {
flex-basis: 0;
flex-grow: 999;
min-width: calc(var(--sidebarContentMin) - var(--sidebarSpace));
}
`;
}
get sideStyle() {
return html`
<style>
:host > ::slotted(*) > ::slotted(*) {
${this.sideWidth ? 'flex-basis: var(--sidebarWidth)' : ''}
}
:host > ::slotted(*) > ${this.side !== 'left' ? '::slotted(*:first-child)' : '::slotted(*:last-child)'} {
flex-basis: 0;
flex-grow: 999;
min-width: calc(var(--sidebarContentMin) - var(--sidebarSpace));
}
</style>
`;
}
static get properties() {
return {
side: { type: String },
sideWidth: { type: String },
contentMin: { type: String },
space: { type: String },
x: { type: String },
noStretch: { type: Boolean, reflect: true, attribute: true }
};
}
updated(changedProperties) {
if (changedProperties.has("space")) {
this.style.setProperty("--sidebarSpace", this.space);
} else if (changedProperties.has("contentMin")) {
this.style.setProperty("--sidebarContentMin", this.contentMin);
} else if (changedProperties.has("sideWidth")) {
this.style.setProperty("--sidebarWidth", this.sideWidth);
}
}
constructor() {
super();
this.side = "left";
this.contentMin = "50%";
if (this.children[0].children.length != 2) {
console.error(`Should have exactly two children..`);
}
}
render() {
if (!this.contentMin.includes('%')) {
console.warn('Minimum content should be in percentages to avoid overflows.');
}
return html`${this.sideStyle}<slot></slot>`;
}
}
customElements.define('test-sidebar2', Sidebar2);
解决方案
如果您访问https://lit-element.polymer-project.org/guide/styles#style-the-components-children并稍微阅读一下页面,您会发现以下内容:
请注意,只有直接开槽的孩子才能使用 ::slotted() 设置样式。
<my-element>
<div>Stylable with ::slotted()</div>
</my-element>
<my-element>
<div><p>Not stylable with ::slotted()</p></div>
</my-element>
所以我不认为像这样的选择器:host > ::slotted(*) > ::slotted(*)
会起作用。
推荐阅读
- java - 如何加密 java 对象并将其作为字符串在 URL 中发送?
- python - 相对于其他方法,魔术方法应该放在类中的什么位置?
- uwp - ADAL AcquireToken Windows 身份验证 UWP
- c# - 如何在 LeMP 输出中保留换行符
- google-cloud-platform - 部署 Grafana 时的未知选项“-L3000:localhost:3000”
- r - 解析 R 中的特定元素
- python - 简单时尚 MNIST 分类任务中的梯度消失
- python - 与本机库相比,在 Python 中使用 os.system('curl ...') 真的不安全吗?
- python - 直方图隐藏空箱
- javascript - js中的id值不能引用文本框