[JavaScript] 바닐라 자바스크립트로 웹 컴포넌트 만들기

자바스크립트만 사용해서(제이쿼리, 리액트, 뷰 등을 사용하지 않고) 웹 컴포넌트를 만드는 방법에 대해 알아보겠습니다.
바닐라 자바스크립트란?
프레임워크나 라이브러리가 포함되지 않은 순수한 자바스크립트를 의미합니다.
웹 컴포넌트
HTML 엘리먼트를 위해 만들어진 재사용 가능한 자바스크립트 코드를 의미합니다. 재사용이 가능하다는 것은 코드가 함수 등으로 작성되어 언제든 임의의 HTML 엘리먼트에 적용할 수 있다는 것을 의미합니다.
TLDR;
- 함수를 이용해 만드는 방법은 쉽고 빠르게 작성할 수 있어 웹 컴포넌트를 구현할 때 가장 많이 쓰이는 방식
- MutationOvserver는 IE11 이하 버전을 지원하지 않으며, 모든 엘리먼트의 추가 / 제거를 감시해 성능 문제를 일으킬 수 있음
- 커스텀 엘리먼트를 만드는 방법은 깔끔하지만, 커스텀 엘리먼트를 중첩해서 사용할 수 없음.
함수를 이용한 방법
함수를 실행해 엘리먼트에 자바스크립트를 적용하는 방법입니다.
<span class="custom">Click me</span>
function customComponent(elements) { for (const element of elements) { element.onClick = function() { element.textContent = "Already clicked"; }; } } customComponent(document.querySelectorAll(".custom"));
위 방법은 동적으로 자바스크립트에서 HTML을 생성하는 경우, 함수를 실행시켜주는 것을 잊어버린다면 적용이 되지 않는다는 단점이 있습니다. 하지만, 간단하게 구현할 수 있고 호환성 문제와 성능 저하 문제 없이 구현할 수 있다는 장점이 있어서 웹 컴포넌트를 구현할 때 많이 쓰이는 방법입니다.
MutationObserver를 이용한 방법
MutationOvserver
를 이용해 class
나 dataset
과 같은 HTML 속성 값의 변화를 감지하는 방법이 있습니다.
MutationObserver이란?
함수를 이용해서 엘리먼트에 자바스크립트를 적용하는 방법은 동적으로 추가된 엘리먼트를 놓칠 수 있다는 문제가 있습니다. 이러한 문제를 피하기 위해 DOM의 변화를 감지하는 MutationObserver
를 사용할 수 있습니다.
기본적인 사용 방법은 다음과 같습니다.
// 변화를 감지할 노드를 선택 const targetNode = document.querySelector(".target"); // 변화 감지에 대한 옵션 설정 const config = { attributes: true, childList: true, subtree: true }; // 변화가 감지될 때 실행할 콜백 함수 const callback = function (mutationList, observer) { for (const mutation of mutations) { if (mutation.type === "attributes") { console.log(mutation.attributeName + ' 속성이 수정되었습니다.'); } else if (mutation.type === "childList") { console.log('자식 노드가 추가되거나 제거되었습니다.'); } } }; // 실행 const observer = new MutationObserver(callback); observer.observe(targetNode, config);
disconnect
함수를 이용해 관찰을 중지할 수 있습니다.
observer.disconnect();
option 설정
사용하는 옵션에 따라 mutation의 실행 방법이 달라지므로 정확한 설정 값이 필요합니다.
속성 | 값 | 설명 |
childList | true / false | 대상 노드의 하위 요소가 추가되거나 제거되는 것을 감지합니다. |
attributes | true / false | 대상 노드의 속성 변화를 감지합니다. |
characterData | true / false | 대상 노드의 데이터 변화를 감지합니다. |
subtree | true / false | 대상의 하위의 하위의 요소들까지의 변화를 감지합니다. |
attributeOldValue | true / false | 변화 이전의 속성 값을 기록합니다. |
characterDataOldValue | true / false | 변화 이전의 데이터 값을 기록합니다. |
attributeFilter | ["...", "..."] | 모든 속성의 변화를 감지할 필요가 없는 경우 속성을 배열로 설정합니다. |
위 표는 모든 옵션에 대한 설명입니다.
구현 방법
<span class="custom">Click me</span>
for (const element of document.querySelectorAll(".custom")) { element.onClick = function() { element.textContent = "Already clicked"; }; } const config = { attributeFilter: ["class"], childList: true, subtree: true }; const callback = function (mutations) { for (const mutation of mutations) { if (mutation.type === "attributes") { if (mutation.target.classList.contains("custom")) { mutation.target.onclick = function() { element.textContent = "Already clicked"; }; } else { mutation.target.onClick = null; } } else if (mutation.type === "childList") { for (const addedNode of mutation.addNodes) { if (addedNode instanceof Element) { if (addNode.classList.contains("custom")) { addedNode.onclick = function() { element.textContent = "Already clicked"; }; } } } } } }; new MutationOvserver(callback) .observe(document, config);
함수와 비교했을 때 코드가 많이 복잡하고 한 눈에 보기 어렵다는 단점이 있습니다. 엘리먼트의 속성 값의 변화와 자식 엘리먼트의 변화에 따라 다루는 방법이 달라 코드가 매우 길어집니다.
또한, 위 방법은 모든 엘리먼트의 추가 / 제거를 감지해서 반복적으로 엘리먼트가 추가되거나 제거되는 상황에서는 성능 문제를 일으킬 수 있으며, IE11 이하 버전에서는 동작하지 않습니다.
customElements를 이용한 방법
customElements
를 이용해 웹 컴포넌트를 구현할 수 있습니다.
customElements란?
Window
인터페이스 내 customElements
속성은 요소를 등록하거나 이전에 등록한 요소에 대한 정보를 받아올 수 있는 CustomElementRegistry
객체의 참조를 반환할 수 있습니다.
예제
customElements
를 사용하는 가장 흔한 예시는 새로운 요소를 등록하기 위해 CustomElementRegistry.define()
메소드에 적용하는 것입니다.
const customElementRegistry = window.customElements; customElementRegistry.define("my-custom-element", MyCustomElement);
다음과 같이 줄여서 사용할 수 있습니다.
customElements.define( "element-details", class extends HTMLElement { constructor() { super(); const template = document .getElementById("element-details-template") .content; const shadowRoot = this.attachShadow({mode: "open"}) .appendChild(template.cloneNode(true)); } } );
구현
<custom-element>Click me</custom-element>
customElements.define( "custom-element", class extends HTMLElement { constructor() { super(); this.onclick = function() { element.textContent = "Already clicked"; }; } } );
customElements
를 이용한 방법은 코드의 가독성도 좋고 깔끔하다는 장점이 있지만, 여러 엘리먼트에 동일한 커스텀 엘리먼트를 중첩해서 적용할 수 없다는 단점이 있습니다.
'Advanced > JavaScript' 카테고리의 다른 글
[JavaScript] 함수를 콜백으로 전달할 때 (0) | 2021.02.12 |
---|---|
[JavaScript] Virtual DOM 만들기 (2) | 2021.01.26 |
[JavaScript] Element를 저장 및 불러오기 (0) | 2021.01.04 |
[JavaScript] Selection과 Range (0) | 2020.12.29 |
[JavaScript] 캘린더 만들기 - 한 주 구하기 (0) | 2020.12.20 |
댓글
이 글 공유하기
다른 글
-
[JavaScript] 함수를 콜백으로 전달할 때
[JavaScript] 함수를 콜백으로 전달할 때
2021.02.12메일로 한 통의 미디엄 글을 받았습니다. 해당 글의 경우 parseInt를 map에 전달했을 때 이상한 값이 나온다는 내용이었는데 코드는 다음과 같습니다. ['1', '7', '11'].map(parseInt); // [1, NaN, 3] ['1', '7', '11'].map(Number); // [1, 7, 11] 위와 같이 나오는 이유에 대한 글인데, parseInt와 Number의 매개변수는 각각 다음과 같습니다. parseInt: string, radix Number: value Number에서는 잘 동작하지만 parseInt에서는 값의 결과가 깨지는 이유는 array.map의 콜백으로 parseInt를 사용하는 것이 함수 설계에 맞지 않기 때문입니다. 무엇이 문제일까? const numList … -
[JavaScript] Virtual DOM 만들기
[JavaScript] Virtual DOM 만들기
2021.01.26React 공식 홈페이지에서는 Virtual DOM을 'UI로 표현될 객체를 가상 메모리에 저장하여 라이브러리에 의해 실제 DOM으로 동기화 하는 개념'으로 표현하고 있습니다. 여기서 UI로 표현될 객체는 DOM을 의미하며, 라이브러리는 VirtualDOM을 렌더링해주는 라이브러리를 의미합니다. TLDR 리액트는 두 가지 전략을 사용해 diff 알고리즘을 기존의 O(n^3)에서 O(n)으로 성능 개선을 하였습니다. 리액트에서 사용하는 Virtual DOM을 바닐라 자바스크립트로 충분히 구현 가능하다는 것을 알 수 있습니다(본 게시글의 Virtual DOM 코드는 리액트의 Virtual DOM diff 알고리즘과 많이 다릅니다). 리액트를 사용할 때 데이터의 변화에 따른 화면 변화의 흐름을 이해한다면 Vi… -
[JavaScript] Element를 저장 및 불러오기
[JavaScript] Element를 저장 및 불러오기
2021.01.04[JavaScript] Element를 저장 및 불러오기 이 글은 Range 값을 저장하기 위해 코드를 작성하다가 localStorage에 Element 정보가 저장되지 않아서 이를 해결하기 위한 트러블 슈팅을 정리한 글입니다. TLDR; Range 값을 저장 후 불러오고 싶은데, Element 값을 저장할 수 없었습니다. JSON.stringify를 사용해서 Element를 문자열 값으로 수정하려 하더라도 빈 객체가 반환되었습니다. 이를 해결하기 위한 방법을 알아보던 중, 페이지의 Element에 data-key를 모두 설정한 뒤, 해당 data-key를 저장하는 방법도 있었습니다. 하지만, 이 방법의 경우 페이지 내에서 변경이 일어난다면 data-key 값이 이전과 같을 것이라 보장할 수 없습니다. … -
[JavaScript] Selection과 Range
[JavaScript] Selection과 Range
2020.12.29[JavaScript] Selection과 Range JavaScript를 사용해 document 내의 선택된 항목에 접근해서 DOM 노드의 전체 또는 선택된 부분을 가져오는 등의 작업을 할 수 있습니다. Selection과 Range는 WYSIWYG 에디터, 리치 에디터 등을 구현할 때 매우 중요하게 사용됩니다. TLDR document 내의 선택된 콘텐츠의 정보를 가져오기 위해 Selection과 Range 객체를 사용할 수 있습니다. input, textarea 엘리먼트의 경우, element.selectionStart 및 element.selectionEnd 값을 설정해 조작할 수 있습니다. Selection 및 Range 가져오기 const selection = document.getSelect…
댓글을 사용할 수 없습니다.