IT일상

[Web Component] Custom Element에서 slot 사용하기 본문

프론트엔드

[Web Component] Custom Element에서 slot 사용하기

solo5star 2023. 3. 6. 15:45
리뉴얼 된 블로그로 보기: https://solo5star.dev/posts/28/

 

<body>
  <p>안녕하세요. 오늘도 좋은 날씨네요!</p>
  <my-modal>
    <!-- 여기에 모달의 내용을 넣고 싶다 -->
  </my-modal>
</body>

내용이 비어 있는 모달 창입니다.

my-modal이라는 Custom Element를 만들었습니다. 모달의 모양만 가지고 있는 엘리먼트인데, 그럼 모달안의 내용은 어떻게 전달할까요?

 

 

Light DOM

<body>
  <p>안녕하세요. 오늘도 좋은 날씨네요!</p>
  <my-modal>
    <h1>오늘 날씨는 15도입니다.</h1>
    <p>기온이 많이 올라갔지만 따뜻하게 입는 것 잊지 마세요!</p>
  </my-modal>
</body>
customElements.define('my-modal', class MyModal extends HTMLElement {
  constructor() {
    super();
    this.childrenHTML = this.innerHTML;
    this.innerHTML = `
      <style>
        dialog::backdrop {
          background: rgba(0, 0, 0, 0.3);
        }
      </style>

      <dialog>
        <form method="dialog">
          ${this.childrenHTML}
          <button>확인</button>
        </form>
      </dialog>
    `;
  }

  connectedCallback() {
    this.querySelector('dialog').showModal();
  }
});

Shadow DOM을 사용하지 않을 시, <my-modal> 내의 HTML은 innerHTML로 접근할 수 있습니다.

 

임시로 this.childrenHTML에 저장한 후 위와 같이 템플릿에 삽입할 수 있습니다.

(childrenHTML은 임의로 정한 이름이기 때문에, 다른 걸로 바꾸어 사용해도 무관합니다)

 

 

Shadow DOM

만일 Shadow DOM을 사용한다면 위의 방법대로 할 수 있을까요?

 

Shadow DOM의 경우, slot element를 사용하면 됩니다.

 

<body>
  <p>안녕하세요. 오늘도 좋은 날씨네요!</p>
  <my-modal>
    <h1>오늘 날씨는 15도입니다.</h1>
    <p>기온이 많이 올라갔지만 따뜻하게 입는 것 잊지 마세요!</p>
  </my-modal>
</body>
customElements.define('my-modal', class MyModal extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <style>
        dialog::backdrop {
          background: rgba(0, 0, 0, 0.3);
        }
      </style>

      <dialog>
        <form method="dialog">
          <slot></slot> <!-- host의 내용을 여기에 표시 -->
          <button>확인</button>
        </form>
      </dialog>
    `;
  }

  connectedCallback() {
    this.shadowRoot.querySelector('dialog').showModal();
  }
});

<my-modal> 내의 html 내용은 <slot></slot> 에 표시됩니다.

 

 

slot에 이름 지정

slot에는 이름을 지정할 수도 있습니다.

 

  <my-modal>
    <h1 slot="title">오늘 날씨는 15도입니다.</h1>
    <p slot="content">기온이 많이 올라갔지만 따뜻하게 입는 것 잊지 마세요!</p>
  </my-modal>
customElements.define('my-modal', class MyModal extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <style>
        dialog::backdrop {
          background: rgba(0, 0, 0, 0.3);
        }
      </style>

      <dialog>
        <form method="dialog">
          <slot name="title"></slot>
          <hr >
          <slot name="content"></slot>
          <button>확인</button>
        </form>
      </dialog>
    `;
  }

  connectedCallback() {
    this.shadowRoot.querySelector('dialog').showModal();
  }
});

이름을 지정하면, slot을 여러 개 만들어 어떠한 위치에 삽입을 할 것인지도 결정할 수 있습니다.

 

 

::slotted()

::slotted() pseudo selector를 사용하면, slot 내의 엘리먼트에 대해 스타일을 지정할 수 있습니다.

 

  <my-modal important>
    <h1 slot="title">오늘 날씨는 15도입니다.</h1>
    <p slot="content">기온이 많이 올라갔지만 따뜻하게 입는 것 잊지 마세요!</p>
  </my-modal>
customElements.define('my-modal', class MyModal extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <style>
        dialog::backdrop {
          background: rgba(0, 0, 0, 0.3);
        }

        :host([important]) ::slotted(h1) {
          text-decoration: underline;
          color: blue;
        }
      </style>

      <dialog>
        <form method="dialog">
          <slot name="title"></slot>
          <hr >
          <slot name="content"></slot>
          <button>확인</button>
        </form>
      </dialog>
    `;
  }

  connectedCallback() {
    this.shadowRoot.querySelector('dialog').showModal();
  }
});

 

 

맺음말

slot 기능을 사용하면 외부에서 <my-button>, <my-alert> 등의 엘리먼트들을 조합하여 사용할 수 있습니다. 따라서 컴포넌트를 재사용하는 데에 큰 도움이 되니 잘 사용하면 좋을 것 같습니다.

Comments