안녕하세요. 이번 시간에는 DOM의 재밌는 기능 중 하나인 MutationObserver 객체를 소개합니다! DOM 요소(태그같은 것)에 발생하는 변경사항을 감지할 수 있습니다. IE11부터 지원됩니다. 바로 코드를 보시죠.
<body>
<div id="zerocho-changeable"></div>
<button id="attributes">attributes</div>
<button id="childList">childList</div>
</body>
먼저 html 태그입니다. 버튼들을 누르면 div 태그에 변경이 생기고, MutationObserver 객체가 나중에 변경된 내용을 기록할 것입니다. 스크립트도 같이 보시
var target = document.getElementById('zerocho-changeable');
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(mutation);
});
});
var config = {
attributes: true,
childList: true,
characterData: true,
subtree: true || null,
attributeOldValue: true || null,
characterDataOldValue: true || null,
}; // 감시할 내용 설정
observer.observe(target, config); // 감시할 대상 등록
document.getElementById('attributes').addEventListener('click', function() {
target.setAttribute('class', 'zerocho-newclass'); // 대상에 변경 발생
});
document.getElementById('childList').addEventListener('click', function() {
target.textContent = 'zerocho'; // 대상에 변경 발생
});
MutationObserver 생성자에서 새로운 옵저버(스타크래프트의 옵저버와 스펠링이 같습니다)를 만들어냅니다. observe라는 메서드가 있는 객체들은 감시자 역할로, 감시하는 대상에 변경사항이 발생할 때마다 미리 정해둔 동작을 수행합니다. observe 메서드에 첫 번째 인자로 감시할 대상을, 두 번째 인자로 어떤 것을 감시할 지에 대한 옵션을 넣어주면 됩니다.
옵션들을 잠깐 살펴볼까요? attributes
는 태그의 속성(id, class, value와 같은)이 변경되는 것을 감지합니다. 추가적으로 attributeOldValue
가 true면 변경전 속성값도 보여줘서 유용합니다. 위에 예제에는 넣지 않았지만 attributeFilter
도 있는데 ['class']
같은 값(배열 형식이어야 합니다)을 주면 class 속성 변경만 감시합니다. childList
속성은 태그의 자식 태그들이 변경되는지를 추적하고, characterData
는 태그의 텍스트 데이터가 변경되는지(characterDataOldValue
는 변경전 데이터도 보여주겠죠? 단, childList 속성과 겹치기 때문에 chidList를 false로 해야 characterData가 기록됩니다.)를, subtree
는 감시 대상의 자식 태그에 변경이 일어나도 변경점을 기록해줍니다.
콘솔에 찍히는 결과는 다음과 같은 객체입니다. 어떤 부분이 변경되었는지 자세하게 알려줍니다.
MutationRecords = {
addedNodes: [], // 추가된 자식 노드,
attributeName: null, // 변경된 속성명
attributeNamespace: null, // 변경된 속성네임스페이스
nextSibling: null, // 다음 형제 태그
previousSibling: null, // 이전 형제 태그
oldValue: null, // 변경전 값
removedNodes: [], // 제거된 자식 노드
target: Element, // 대상 태그
type: 'attributes' || 'childList' || 'characterData' // 어떤 종류가 변경되었는지
}
예를 들어 attributes 버튼을 누르면 다음과 같이 기록됩니다.
{
attributesName: 'class',
target: div#zerocho-changeable.zerocho-newclass,
type: 'attributes',
// 나머지는 기본값
}
마지막으로 옵저버의 disconnect 메서드를 사용해서 감시를 중단할 수 있습니다. 중단 후에 언제든지 다시 observe 메서드로 감시를 재개할 수 있습니다.
observer.disconnect(); // 감시 중지
옵저버 객체들은 활용 방법이 무궁무진합니다. 예를 들어, 특정 태그에 클래스가 변경되거나, 어떠한 자식 태그가 추가되었을 때 미리 지정한 동작을 하게 할 수 있습니다. CSS를 바꿀 수도 있고, setTimeout으로 몇 초 뒤에 추가적인 행동을 수행하게 할 수도 있고, 이벤트 리스너같은 용도로 사용할 수 있을 것 같습니다.
단, 지나친 남용은 옵저버간의 연쇄 작용(서로가 서로를 감시해 하나가 바뀌었을 때 연달아서 모든 게 바뀌어버림)을 심화시켜 디버깅을 매우 어렵게만들 수 있습니다.
해당 위의 코드중에
mutations.forEach(function(mutation) {
console.log(mutation);
});
이 부분은 타갯노드가 똑같은게 여러개 들어오던데 저는 어트리뷰터 감지만 적용을했구요..
왜 굳이 동일한 타갯노드가 여러개 들어오는지 궁금합니다.