바닐라 자바스크립트로 컴포넌트 분리(web components, shadow dom)
본문 바로가기

Frontend

바닐라 자바스크립트로 컴포넌트 분리(web components, shadow dom)

실무프로젝트후 주의할점이 생김!

예를들어 모달컴포넌트를 만들었다고 가정할경우

  • 컴포넌트는 같되 컴포넌트 아이디명을 다르게하여 중복을 피해야함
  • 모달안의 내용을 선택할때 다른모달안의 아이디명과 중복될수있으므로 주의해야함 document.querySelector('#컴포넌트아이디 #아이디)로해야 문제가 해결됨

 

--

 

바닐라자바스크립트 클래스를 활용해 컴포넌트를 분리할수있다.

리엑트나 다른거필요없음, 단 클래스이기때문에 인터넷익스플로어에서는 안됨..

 

기초를 먼저 살펴보고 명찰(name tag)만들기 토이 프로젝트로 web component를 알아보자~!!

기초

main.js

class UserCard extends HTMLElement{
  constructor(){
    super();
    this.innerHTML = `word here`
  }
}

window.customElements.define('user-card', UserCard)

HTMLElement를 상속해서 생성자함수를 실행해 초기화를 하는데

부모에있는 것들을 가져오고(super)

생성자의 내부는 'word here'을 표시한다는 뜻이다.

 

커스텀된 element를 defind하면 html내에서 아래처럼 사용할수있음.

 

index.html

  <user-card></user-card>
  <script src="userCard.js"></script>

 

index.html 열면 defined된 UserCard클래스의 내부를 볼수있다.

 

getAttribute

name, value같은 태그내 속성을 이용할수있음

  <user-card name="John"></user-card>
  <script src="userCard.js"></script>

이름을 John이라 설정하고

main.js에 돌아가 this.getAttribute('name')를 하면 값을 불러올 수 있다.

class UserCard extends HTMLElement{
  constructor(){
    super();
    this.innerHTML = `${this.getAttribute('name')}`
  }
}

window.customElements.define('user-card', UserCard)

우리는 여기서부터 다양한 이름을가진 카드들을 만들수게되는 것이다!

shadow  dom 만들기

이걸왜써야하는지보니깐 특정태그내에서만 커스텀 스타일등이 필요할때 사용하는듯하다.

예를들어 전체 스타일의 h3태그 색상이 빨간색으로 되어있을때(전역스타일)

내가만든 user-card태그내에서만 h3태그 색상은 오렌지색으로 하고싶을때 사용할수있다고함

물론 인라인스타일로 해도되지만 캡슐화를 하는것이 목적이기때문에 shadow dom을 활용한다.

 

const template = document.createElement('template');
template.innerHTML = `
  <style>
    h3{
      color:orange;
    }
  </style>
  <div class="user-card">
    <h3></h3>
  </div>
`

class UserCard extends HTMLElement{
  constructor(){
    super();

    //shadow DOM
    this.attachShadow({mode:'open'});
    this.shadowRoot.appendChild(template.content.cloneNode(true));
    this.shadowRoot.querySelector('h3').innerText = this.getAttribute('name')

    // this.innerHTML = `<h3>${this.getAttribute('name')}</h3>`
  }
}

window.customElements.define('user-card', UserCard)

shadow-root가 있는 웹구성요소가 표시되고 적용된 스타일은 외부에 영향을 미치지않는다.

이제 컬러는 지워보고 본격적으로 스타일을 입혀본다.

img attribute도 추가해보고 shadow dom을 통해 카드스타일을 꾸며보았다.

const template = document.createElement('template');
template.innerHTML = `
  <style>
    .user-card{
      background:#f4f4f4;
      width:500px;
      display:grid;
      grid-template-columns:1fr 2fr;
      grid-gap:10px;
      margin-bottom:15px;
      border-bottom:darkorchid 5px solid;
    }
    .user-card img{
      width:100%;
    }

    .user-card button{
      cursor:pointer;
      background:darkorchid;
      color:#fff;
      border:0;
      border-radius:5px;
      padding:5px 10px;
    }
  </style>
  <div class="user-card">
    <img />
    <div>
     <h3></h3>
     <div class="info">
      <p>EMAIL</p>
      <p>PHONE</p>
     </div>
     <button id="toggle-info">Hide Info</button>
    <div>
  </div>
`

class UserCard extends HTMLElement{
  constructor(){
    super();

    //shadow DOM
    this.attachShadow({mode:'open'});
    this.shadowRoot.appendChild(template.content.cloneNode(true));
    
    // <h3></h3>
    this.shadowRoot.querySelector('h3').innerText = this.getAttribute('name')

    //<img>
    this.shadowRoot.querySelector('img').src = this.getAttribute('avatar');


    // this.innerHTML = `<h3>${this.getAttribute('name')}</h3>`
  }
}

window.customElements.define('user-card', UserCard)

 

slot

EMAIL

PHONE을 대체하는 간단한 방법을 알아보자.

방금했던 template.innerHTML 안에 p태그를 아래와같이 변경해준다.

template.innerHTML = `
...
      <p><slot name="email"/></p>
      <p><slot name="phone"/></p>
...
`
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>web components example</title>
  <style>
    h3{color:red}

  </style>
</head>
<body>
  <h3>Hello World</h3>
  <user-card name="John" avatar = "https://item.kakaocdn.net/do/4b989c7a0e344b9709589556ffd80538617ea012db208c18f6e83b1a90a7baa7">
    <div slot="email">john@gmail.com</div>
    <div slot="phone">010-1234-3513</div>
  </user-card>
  <user-card name="Jane" avatar = "https://item.kakaocdn.net/do/75e402fe47122beeed4e853466d8b943f43ad912ad8dd55b04db6a64cddaf76d">
    <div slot="email">jane@gmail.com</div>
    <div slot="phone">010-3334-3683</div>
  </user-card>
  <script src="userCard.js"></script>
</body>
</html>

마무리, 이벤트 넣기

Hide Info버튼을 클릭하면 이메일과 연락처가 사라지고 버튼이름이 Show Info로 변경되도록 해보자.

 

const template = document.createElement('template');
template.innerHTML = `
  <style>
    .user-card{
      background:#f4f4f4;
      width:500px;
      display:grid;
      grid-template-columns:1fr 2fr;
      grid-gap:10px;
      margin-bottom:15px;
      border-bottom:darkorchid 5px solid;
    }
    .user-card img{
      width:100%;
    }

    .user-card button{
      cursor:pointer;
      background:darkorchid;
      color:#fff;
      border:0;
      border-radius:5px;
      padding:5px 10px;
    }
  </style>
  <div class="user-card">
    <img />
    <div>
     <h3></h3>
     <div class="info">
      <p><slot name="email"/></p>
      <p><slot name="phone"/></p>
     </div>
     <button id="toggle-info">Hide Info</button>
    <div>
  </div>
`

class UserCard extends HTMLElement{
  constructor(){
    super();

    this.showInfo = true

    this.attachShadow({mode:'open'});
    this.shadowRoot.appendChild(template.content.cloneNode(true));

    this.shadowRoot.querySelector('h3').innerText = this.getAttribute('name')
    this.shadowRoot.querySelector('img').src = this.getAttribute('avatar');

  }

  toggleInfo(){
    // alert('123')
    this.showInfo = !this.showInfo

    const info = this.shadowRoot.querySelector('.info')
    const toggleBtn = this.shadowRoot.querySelector('#toggle-info')

    if(this.showInfo){
      info.style.display = 'block';
      toggleBtn.innerText = 'Hide Info'
    }else{
      info.style.display = 'none';
      toggleBtn.innerText = 'Show Info'
    }
  }

  connectedCallback(){
    this.shadowRoot.querySelector('#toggle-info').
    addEventListener('click',()=>this.toggleInfo())
  }

  disconnectedCallback(){
    this.shadowRoot.querySelector('#toggle-info').
    removeEventListener('click',()=>this.toggleInfo())
  }
}

window.customElements.define('user-card', UserCard)

 

이외에도 데이터를 호출하는 방법도 있다고하는데 배우면 좀더 추가할예정!

 

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes

 

Classes - JavaScript | MDN

Classes are a template for creating objects. They encapsulate data with code to work on that data. Classes in JS are built on prototypes but also have some syntax and semantics that are not shared with ES5 class-like semantics.

developer.mozilla.org

https://www.youtube.com/watch?v=PCWaFLy3VUo 

반응형