[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 -
[JavaScript] Virtual DOM 만들기
[JavaScript] Virtual DOM 만들기
2021.01.26 -
[JavaScript] Element를 저장 및 불러오기
[JavaScript] Element를 저장 및 불러오기
2021.01.04 -
[JavaScript] Selection과 Range
[JavaScript] Selection과 Range
2020.12.29