CSS n-선택기 변수


사진 작성자Mario GoghUnsplash
적어도 2021년 6월에 내가 이 줄을 쓸 때, CSS 변수를 사용하면 미디어 조회나 선택기에서 지원되지 않는다. 예를 들어 :nth-child(var(--my-variable)) 는 작용하지 않는다.
이것은 약간 불행하지만, 결코 해결할 수 없는 것은 아니다.최근의 일부 개발에서, 나는 이 제한을 빙빙 돌려서 DOM의 style 요소를 웹 구성 요소에 주입하여 DeckDeckGo의 코드 블록에 애니메이션을 설정하도록 했다.

소개하다.


엄밀히 말하면, 아래의 기교는 웹 구성 요소에 적용되지 않고, 어떤 요소에도 적용될 수 있다.지금까지 나는 단지 이런 기술을 사용하고 있을 뿐이다😜.
나는 우선 일반 구성 요소의 도움으로 이 생각을 보여주고, 문장의 끝에는 같은 방법을 사용하지만, StencilJS 기능 구성 요소로 실현할 것이다.

이 자습서의 목표


우리는 웹 구성 요소를 개발하여 <ul/> 목록을 보여주고 항목의 디스플레이에 애니메이션을 설정할 수 있습니다.

어셈블리를 로드한 후에는 DOM에서 의미 요소가 추가되거나 삭제되지 않습니다.애니메이션은 수정style을 통해 이루어질 것이다. 더욱 정확히 말하면 선택된 li:nth-child(n)에 서로 다른 스타일을 적용한다.

바닐라 JS


인터넷 없이 창의력을 보여주기 위해 index.html 페이지를 만들었습니다.그것은 우리가 곧 개발할 바닐라 성분을 소모할 것이다.우리는 애니메이션을 터치하기 위해 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의 파일에서 웹 구성 요소를 만들었습니다.애니메이션이 없습니다.우리는 이것open이 그림자 DOMshadowRoot에 접근할 수 있다고 성명했다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 . 에서 예시를 열면 숨겨진 내용을 포함하는 구성 요소와 아직 효력이 발생하지 않은 단추를 찾을 수 있습니다.볼 것도 없지만 시작일 뿐이야😁.

애니메이션을 개발하기 위해서 우리는 디스플레이li를 추적해야 한다. 이것이 바로 우리가 구성 요소에 상태(index를 추가해야 하는 이유이다.
class MyComponent extends HTMLElement {
    index = 0;

    constructor() {
...
덕분에 우리는 next() 방법을 실현할 수 있다. 이 방법은 우리가 이전에 HTML 페이지에 추가한 단추에서 호출된 것이다.

Not my most beautiful code ever. Let’s agree it has only a demo purpose 😅.


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 을 설정한 다음, selector 스타일을 적용하기 위해 CSS opacity 를 만듭니다.요컨대, 이것은 우리가 사용할 수 없는 CSS 변수를 대체할 것이다.
그런 다음 웹 구성 요소의 섀도우 컨텐트에 적용된 애니메이션 전용 스타일이 포함되어 있는지 확인합니다.있으면, 새 값 선택기로 스타일을 업데이트하고, 없으면, 새 스타일 표시자를 만듭니다.
이 방법을 호출할 때마다 새 style 이 적용되기 때문에 다른 li:nth-child(n) 이 표시됩니다.
만약에 우리가 브라우저를 다시 켜서 시도를 한다면 우리의 단추next를 눌렀을 때 항목은 애니메이션이어야 한다. 만약에 inspector의 구성 요소를 더 살펴보면 음영style 요소가 매번 방법이 호출될 때마다 변화하는 것을 주의해야 한다.

거푸집


우리는 같은 예로 즐거움을 배가하지만 StencilJS 기능 구성 요소를 사용한다🤙.

You can start a new project with the command line npm init stencil


우리는 완전히 같은 구성 요소를 개발하고 있기 때문에 이전의 HTML 내용 (이 구성 요소를 설명하고 프로젝트 button 에 하나 ./src/index.html 를 복사할 수 있습니다. 아주 작은 차이만 있을 뿐입니다. 방법 next() 은 반드시 설명하고 async-await 호출을 사용해야 합니다.이것은 템플릿의 가장 좋은 실천이며 구성 요소의 공공 방법은 반드시 async이어야 한다.
<!DOCTYPE html>
<html dir="ltr" lang="en">
  <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 를 통해 우리도 같은 결과를 얻어야 한다😉.

강조 표시된 li 를 추적하기 위해서는 상태와 함수 state 가 필요합니다.우리는 둘 다 구성 요소에 추가할 것이다.
@State()
private index: number = 0;

@Method()
async next() {
  this.index = this.index === 3 ? 1 : this.index + 1;
}
Vanilla 구성 요소에 비해, 우리는 bundler를 사용하기 때문에 개발 과정을 간소화하기 때문에, 우리는 스스로 다시 과장할 필요가 없다.state에 대한 수정 사항이 있을 때마다 다시 렌더링이 트리거되고 업데이트해야 하는 노드만 업데이트됩니다.
그럼에도 불구하고 CSS 선택기 변수를 구현해야 합니다.앞에서 말한 바와 같이, 이 목적에서 우리는 기능 구성 요소를 사용할 것이다.그것은 클래스 구성 요소와 함께 일할 수 있지만, 나는 기능 구성 요소가 이 작업에 매우 적합하다고 생각한다.
const Animate: FunctionalComponent<{index: number;}> = ({index}) => {
  return (
    <style>{`
    li:nth-child(${index}) {
      opacity: 1;
    }
  `}</style>
  );
};
이 구성 요소는 style 요소를 보여 줍니다. 이 요소의 값은 we path입니다. 즉, 우리의 state 입니다.
마지막으로, 우리는 기능 구성 요소를 사용하여 우리의 상태 값에 연결해야 한다.이렇게 하면 값이 변경될 때마다 다시 렌더링됩니다.
render() {
  return <Host>
    <Animate index={this.index}></Animate>
    <ul>
      <li>Spine</li>
      <li>Cowboy</li>
      <li>Shelving</li>
    </ul>
  </Host>
}
이렇게 하면 우리는 같은 구성 요소를 복제할 수 있다🥳.

이러한 구성 요소는 단일 코드 블록으로 구성됩니다.
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로 같은 코드를 개발하는 것도 흥미롭다.
무한원까지!
다윗
전화나 제website로 연락 주세요.
다음 프레젠테이션을 수행하십시오DeckDeckGo.

좋은 웹페이지 즐겨찾기