CSS的第n个选择器变量

CSS的第n个选择器变量

文章资讯开源世界2021-06-13 6:29:404070A+A-

CSS的第n个选择器变量  新闻 第1张

一个实用的黑客,使css nth-选择器在web组件中可变。

使用CSS变量,至少当我在2021年6月写这几行的时候,媒体查询或选择器不支持,例如,:nth-child(var(--my-variable)) 不工作。

这有点不幸,但并不是无法解决的。在最近的开发中,我绕过了这个限制,在DOM中注入了style 元素到Web组件中,以使DeckDeckGo中的代码块成为动画。

简介

严格意义上讲,下面的技巧并不局限于Web组件,可能也适用于任何元素。我只是把它用在这种技术?。

我将首先用一个普通的组件来展示这个想法,然后用同样的方法来结束这篇文章,但用StencilJS功能组件来实现。

教程的目标

我们将开发一个Web组件,它可以渲染一个<ul/> 列表,并且可以对其条目进行动画显示。

CSS的第n个选择器变量  新闻 第2张

一旦该组件被加载,将不会从DOM中添加或删除任何语义元素。动画将通过修改style ,更确切地说,是通过在选定的li:nth-child(n) 上应用不同的样式来实现的。

Vanilla JS

为了在没有任何其他东西的情况下显示这个想法,我们创建了一个index.html 页面。它消耗了我们将要开发的Vanilla组件。我们还添加了一个button 来触发动画。

<html>     <head>         <script type="module" src="./my-component.js"></script>     </head>     <body>         <my-component></my-component>         <button>Next</button>         <script>             document               .querySelector('button')               .addEventListener(                  'click',                   () => document.querySelector('my-component').next()               );         </script>     </body> </html> 复制代码

在一个单独的文件中,称为my-component.js ,我们创建了网络组件。在这一点上,没有任何动画。我们声明它是开放的,以便能够访问影子DOM(通过shadowRoot ),我们创建一个样式来隐藏所有li ,并定义transition 。最后,我们添加ul 列表和它的孩子li 。

class MyComponent extends HTMLElement {     constructor() {         super();         this.attachShadow({mode: 'open'});         const style = this.initStyle();         const ul = this.initElement();         this.shadowRoot.appendChild(style);         this.shadowRoot.appendChild(ul);     }     connectedCallback() {         this.className = 'hydrated';     }     next() {         // TODO in next chapter     }     initStyle() {         const style = document.createElement('style');         style.innerHTML = `           :host {             display: block;           }                      li {             opacity: 0;             transition: opacity 0.5s ease-out;           }         `;         return style;     }     initElement() {         const ul = document.createElement('ul');         const li1 = document.createElement('li');         li1.innerHTML = 'Spine';         const li2 = document.createElement('li');         li2.innerHTML = 'Cowboy';         const li3 = document.createElement('li');         li3.innerHTML = 'Shelving';         ul.append(li1, li2, li3);         return ul;     } } customElements.define('my-component', MyComponent); 复制代码

在这一点上,如果我们在浏览器中打开我们的例子 (npx serve .),我们应该发现一个组件,有一个隐藏的内容,还有一个还没有效果的按钮。这没有什么可看的,但这是一个开始?。

CSS的第n个选择器变量  新闻 第3张

为了开发动画,我们必须跟踪所显示的li ,这就是为什么我们要给这个组件添加一个状态(index )。

class MyComponent extends HTMLElement {     index = 0;          constructor() { 复制代码

由于它的存在,我们可以实现next() 方法,也就是我们先前在HTML页面中添加的按钮所调用的方法。

这不是我最漂亮的代码。让我们同意它只有一个演示的目的?。

next() {     this.index = this.index === 3 ? 1 : this.index + 1;     const selector = `       li:nth-child(${this.index}) {         opacity: 1;       }     `;     let style = this.shadowRoot.querySelector('style#animation');     if (style) {         style.innerHTML = selector;         return;     }     style = document.createElement('style');     style.setAttribute('id', 'animation');     style.innerHTML = selector;     this.shadowRoot.appendChild(style); } 复制代码

那里发生了什么?

它首先设置了下一个index ,li ,来显示,并且,创建一个CSSselector 来应用opacity 的样式。简而言之,这取代了我们不能使用的CSS变量。

之后,我们检查我们的Web组件的阴影内容是否已经包含一个专门的样式来应用动画。如果有,我们就用新的值更新样式--选择器,如果没有,我们就创建一个新的样式标签。

每次调用这个方法,都会应用一个新的style ,因此会显示另一个li:nth-child(n) 。

如果我们再次打开浏览器试一试,在点击我们的按钮next ,项目应该是动画的,而且,如果我们进一步观察检查器中的组件,我们应该注意到阴影的style 元素在每次方法调用时都会改变。

CSS的第n个选择器变量  新闻 第4张

StencilJS

让我们用同样的例子来增加乐趣,但是,使用一个StencilJS功能组件?。

你可以用命令行启动一个新的项目npm init stencil

因为我们正在开发完全相同的组件,我们可以在项目的./src/index.html 中复制之前的HTML内容(声明组件并添加一个button ),但只有一个微小的区别,即必须声明方法next() ,并以async - await方式调用。这是一个要求--Stencil的最佳实践,组件的公共方法必须是async 。

<!DOCTYPE html> <html dir="ltr">   <head>     <meta charset="utf-8" />     <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0" />     <title>Stencil Component Starter</title>     <script type="module" src="/build/demo-stencil.esm.js"></script>     <script nomodule src="/build/demo-stencil.js"></script>   </head>   <body>   <!-- Same code as in previous chapter -->   <my-component></my-component>   <button>Next</button>   <script>     document.querySelector('button')        .addEventListener(           'click',            async () => await document                              .querySelector('my-component').next()             );   </script>   <!-- Same code as in previous chapter -->   </body> </html> 复制代码

我们也可以重复前面的步骤,先创建一个组件,它只负责渲染一个ul 列表和隐藏的项目li 。

import { Component, h } from '@stencil/core'; @Component({   tag: 'my-component',   styles: `:host {       display: block;     }     li {       opacity: 0;       transition: opacity 0.5s ease-out;     }   `,   shadow: true, }) export class MyComponent {   render() {     return <ul>       <li>Spine</li>       <li>Cowboy</li>       <li>Shelving</li>     </ul>   } } 复制代码

通过测试组件(npm run start ),我们也应该得到同样的结果?。

CSS的第n个选择器变量  新闻 第5张

为了跟踪要突出显示的li ,我们需要一个状态和,函数state 。我们把这两者都添加到我们的组件中。

@State() private index: number = 0; @Method() async next() {   this.index = this.index === 3 ? 1 : this.index + 1; } 复制代码

与Vanilla组件相比,因为我们使用的是一个简化开发的捆绑器,所以我们不需要自己去处理重新渲染的问题。state 的每一次修改都会触发一次重新渲染,最终更新必须要更新的节点(而且只有那些必须要更新的节点)。

尽管如此,我们还是必须实现CSS选择器变量。为了这个目的,正如简单提到的,我们将使用一个功能组件。它可能与类组件一起工作,但是,我觉得功能组件很适合这项工作。

const Animate: FunctionalComponent<{index: number;}> = ({index}) => {   return (     <style>{`     li:nth-child(${index}) {       opacity: 1;     }   `}</style>   ); }; 复制代码

这个组件为我们作为参数的路径值渲染了一个style 元素,即我们的state 。

最后,我们必须使用功能组件,并将其与我们的状态值绑定。这样做,每次它的值发生变化时,它都会被重新渲染。

render() {   return <Host>     <Animate index={this.index}></Animate>     <ul>       <li>Spine</li>       <li>Cowboy</li>       <li>Shelving</li>     </ul>   </Host> } 复制代码

这已经是它了,我们能够复制同一个组件?。

CSS的第n个选择器变量  新闻 第6张

上述组件只用了一个代码块。

import { Component, FunctionalComponent, h, Host, Method, State } from '@stencil/core'; const Animate: FunctionalComponent<{index: number;}> = ({index}) => {   return (     <style>{`     li:nth-child(${index}) {       opacity: 1;     }   `}</style>   ); }; @Component({   tag: 'my-component',   styles: `:host {       display: block;     }     li {       opacity: 0;       transition: opacity 0.5s ease-out;     }   `,   shadow: true, }) export class MyComponent {   @State()   private index: number = 0;   @Method()   async next() {     this.index = this.index === 3 ? 1 : this.index + 1;   }   render() {     return <Host>       <Animate index={this.index}></Animate>       <ul>         <li>Spine</li>         <li>Cowboy</li>         <li>Shelving</li>       </ul>     </Host>   } } 复制代码

总结

说实话,我不确定这篇文章会不会找到它的读者,也不认为它可能在某一天对某人有用,但是,我喜欢用这招?。此外,用Vanilla JS或Stencil来开发同一段代码的演示也很有趣。


Copyright 百搜论坛 版权所有 精品源码推荐:http://github.crmeb.net/u/defu
#转载请注明出处!

点击这里复制本文地址 以上内容由开源世界整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

开源世界-开源独尊-开源中国-ThinkPHP-CRMEB-商城源码-项目下载站-CMS企业模板-互站网-悟空源码-A5资源网-CSDN-ASP300 © All Rights Reserved.  Copyright 开源世界 版权所有
Powered by 百搜科技 Themes by www.baisou.ltd
联系我们| 关于我们| 留言建议| 网站管理

分享:

支付宝

微信

嘿,欢迎咨询
请先 登录 再评论,若不是会员请先 注册