반응형

만약에 웹 페이지가 열리고 setTimeout으로 2초 후에 이미지 하나가 삽입이 된다고 했을 때

그것을 어떻게 감지할 수 있을까요? 오늘은 그것을 감지할 수 있는 2가지 방법을 공부해보겠습니다.

 

1. Mutation Events

이건 옛날 방식입니다. (IE를 지원해야 하는 경우에는 이것을 활용하면 좋을 것입니다.)* (주의) MDN 문서에서  Mutation Events 리스너를 추가하면 해당 문서에 대한 추가 DOM 수정의 성능이 크게 저하됩니다.(1.5배 - 7배 느려짐) 또한 리스너를 제거해도 손상에 대해서 다시 되돌릴 수 없다고 합니다.

 

- DOM level 2에서 도입되었습니다. 현재는 Deprecated 되었고  Mutation Observer를 권장합니다. 아래는 Mutation Events List입니다. 이것을 활용해서 DOM요소의 추가, 삭제, 속성변경 등을 알아낼 수 있습니다

  • DOMAttrModified
  • DOMAttributeNameChanged
  • DOMCharacterDataModified
  • DOMElementNameChanged
  • DOMNodeInserted
  • DOMNodeInsertedIntoDocument
  • DOMNodeRemoved
  • DOMNodeRemovedFromDocument
  • DOMSubtreeModified

 

코드

function createFunc() { // 2500개의 엘리먼트를 만들어서 삽입.
    	var root = document.querySelector('#root');
        var tempNode = document.createDocumentFragment();
        var createNumber = 2500;
        var i= 0;
        var p;
        while(i < createNumber) {
        	p = (p === undefined) ? document.createElement('p'): p.cloneNode(false);
          p.innerText = 'hi';
          tempNode.appendChild(p);
        	i++;
        }
        root.appendChild(tempNode);
}
    
function handleEvents() {
    	var root = document.querySelector('#root');
      
		root.addEventListener('DOMNodeInserted', function(e) {
      		console.log('돔 변화 감지');
      	});
}
    
function main() {
      handleEvents();
      setTimeout(createFunc, 1000);
}
    
    main();

위의 코드를 실행하면 총 2500개의 "돔 변화 감지"라는 문자열이 console창에 찍힐 것입니다.

 

문제점

1. Mutation Events는 동기적입니다. 이 이벤트가 호출될 때 다른 이벤트가 실행되지 않을 수 있습니다.

많은 노드를 추가하거나 제거하면 어플리케이션이 지연되거나 중단될 수 있습니다.

2. 이벤트는 캡처링, 버블링을 통해 전파됩니다. 캡처링, 버블링은 DOM을 수정하는 다른 이벤트 리스너를 트리거할 수 있습니다. 이렇게 되면 많은 Mutation Events를 일으킬 수 있습니다.  -> javascript쓰레드를 막히게 만들거나 브라우저 충돌이 일어날 가능성이 높습니다.

 

--> 이런 문제점을 해결하기 위해서 나온 것이 Mutation Observer입니다!

반응형

Mutation Observer

이것은 무엇이 다른지 한번 살펴보겠습니다.

  1. Mutation Observer는 비동기적으로 작동합니다. 이벤트가 발생할 때 마다 실행되지 않습니다.
  2. 다른 스크립트가 완료될 때까지 기다립니다.
  3. 배열안에 담아서 기록을 합니다. 모든 노드의 변화를 관찰하거나 특정한 노드만 관찰하는 것이 가능합니다.
  4. 이벤트가 아니기 때문에 다른 이벤트와 충돌을 하지 않아 UI가 멈추거나 브라우저 충돌을 일으킬 가능성이 적습니다.

위에서 본 코드를 Mutation Observer를 이용해서 실행을 하면 과연 console에 2500개가 찍힐까요?

function createFunc() {
    	var root = document.querySelector('#root');
        var tempNode = document.createDocumentFragment();
        var createNumber = 2500;
        var i= 0;
        var p;
        while(i < createNumber) {
          p = (p === undefined) ? document.createElement('p'): p.cloneNode(false);
          p.innerText = 'hi';
          tempNode.appendChild(p);
        	i++;
        }
        root.appendChild(tempNode);
}
    
    
function handleMutationObserver() {
    console.log('옵저버 시작');
    var targetNode = document.querySelector('#root');
    var config = {
      	attributes: true,
        childList: true,
        subtree: true
    };
      
    var callback = function(mutationList) {
      	for(var mutation of mutationList) {
	        console.log('start loop');
			console.log(mutationList); // length -> 2500 array	
        	if(mutation.type === 'childList') {
          	console.log('A child node has been added or removed');
          }
          console.log('end loop');
        }
    }
      
    var observer = new MutationObserver(callback);
      
      observer.observe(targetNode, config);
      
    }
    
function main() {
     handleMutationObserver();
      setTimeout(createFunc, 1000);
}
    
main();

console에는 하나만 출력이 되고 mutationList배열에 2500개가 들어가 있을 것입니다.

 

 

observe 메소드를 실행한 뒤 요소가 변할 때 지금은 선택한 노드의 childList, attributes, subtree를 추적할 수 있도록 했습니다. 하지만 더 다양한 설정들이 가능합니다.

// 타겟 element id -> targeting

MutationRecords = {
  addedNodes: [], // 추가된 자식 노드, ex) NodeList [div]
  attributeName: null, // 변경된 속성명 ex) style, class
  attributeNamespace: null, // 변경된 속성네임스페이스
  nextSibling: null, // 다음 형제 태그
  previousSibling: null, // 이전 형제 태그
  oldValue: null, // 변경전 값  // 이것을 얻고 싶다면 설정값에서 attributeOldValue/characterDataOldValue.
//를 먼저 true로 변경할 것 그렇지 않으면 콜백에 새로운 값만 넘어감.
  removedNodes: [], // 제거된 자식 노드 
  target: div#targeting, // 대상 태그 
  type: 'attributes' || 'childList' || 'characterData' // 어떤 종류가 변경되었는지
}

이런 종류의 config가 있으니 필요한 곳에 잘 갖다 쓰면 될거같습니다.

반응형
반응형

이전의 포스팅에는 부족한 점이 많아 새롭게 글을 적습니다.

 

input 안에 내용 출력하기 전에 알아야할 개념은 DOM 입니다.

 

DOM  - Document Object Model

 

저희는 HTML 을 다루고 있으니 HTML DOM 을 공부해야 합니다.HTML DOM은 HTML 문서에 접근하기 위한 일종의 인터페이스입니다.

 

우선 이 코드를 먼저 보겠습니다.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>input출력</title>
</head>
<body>
    <input type="text" placeholder="내용 입력">
</body>
</html>

이것의 DOM 구조는 이러합니다. 

DOM 구조

이 DOM은 javascript로  제어가 가능합니다. 

- document.getElementById(요소)

- document.querySelector(선택자) 등등 으로 DOM 객체를 제어할 수 있습니다. 

 

그러면 input의 DOM 객체를 접근해보도록 하겠습니다. 아래 코드를 봐주세요.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>input예제</title>
</head>
<body>
    <input  type="text" placeholder="내용 입력" value="안녕하세요?">
    <script>
        const inputElement = document.querySelector('input');
        console.log(inputElement);
    </script>
</body>
</html>

코드 결과

실행을 해보시면 input이라는 이름의 객체가 출력이 됩니다. 내부에 적힌 속성들이 많이 나열되어있는것을 확인하실 수 있습니다.

 

input 안에 내용을 입력을 하면 그 값은 value에 담기게 됩니다.

위의 예제를 실행해보고 속성들을 잘 살펴보면 이런 것이 보일 것입니다.

value값

 자 그럼 아래 코드를 보겠습니다.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <input type="text" placeholder="내용 입력" value="안녕하세요?">
    <script>
        const inputElement = document.querySelector('input');
        console.log(inputElement.value);
    </script>
</body>
</html>

결과

이렇게 Element.value 로 접근이 가능합니다! 

ID 입력받고 출력하는 예제( 가장 많이 하는 실수 버전 )

그러면 초반에 제일 많이 하는 실수에 대해서 코드를 짜보겠습니다.

 

See the Pen by dnjs2618 (@dnjs2618) on CodePen.

 

 

이런 방식으로 하게되면 2개의 문제점이 생깁니다.

1) 새로고침이 된다.

2) 내용을 입력해도 "안녕하세요?"가 출력된다.

 

그렇기 때문에 이런 방식으로 코드를 짜야합니다. 

ID 입력받고 출력하는 예제

See the Pen by dnjs2618 (@dnjs2618) on CodePen.

 

1) 전송 막기

전송 막는 법 ->  이벤트 등록시에 콜백함수에 인자로 e를 적고 e.preventDefault();로 막을 수 있다.다른 방법으로는 return false;가 있다.

 

2) 값 제대로 출력하기

값을 제대로 출력하기 위해서는 '언제 그 값을 받을지?' 가 제일 중요합니다. 지금 코드는 버튼이 눌렸을때 값을 받아오는것입니다. 그렇기 때문에 value를 받아오는 시점이 버튼이 눌렸을때입니다. 

 

 

이상으로 포스팅을 마치겠습니다.

 

부족한점이 많아서 지적해주시면 수정하겠습니다. 감사합니다.

반응형
반응형

자바스크립트 배열 요소 다루기 

  1.  push method
  2.  pop method
  3.  unshift method
  4.  shift method
  5.  splice method
  6.  concat method

자바스크립트에서 배열을 다루다 보면 배열의 요소를 추가하고 삭제하는 일들이 많다. 또한 기존 배열을 바탕으로 새로운 배열을 만들어낼때가 있다. 지금 소개하는 배열요소를 제어하는 메서드를 알아두면 배열요소를 자유자재로 추가하고 삭제할 수 있다.

 

우선 한눈에 알아보기 쉽게 아래 그림을 먼저 보겠다.

 

const myArr = [ 'a', 'b', 'c' ]; 가 있다고 가정하자.

배열 그림

그림을 잘 못그렸지만 각 메서드가 요소를 추가하고 삭제하는 위치에 대해서 간단하게 그려봤다. 아래에 하나하나 차근차근 알아보자.

Array.prototype.push()

push 메서드는 요소를 뒤에서 추가한다. 코드를 통해서 알아보자

const myArr = ['a', 'b', 'c'];
console.log(myArr); // push 전 배열 찍기
myArr.push('d');
console.log(myArr); // push 후 배열 찍기

코드 결과

Array.prototype.pop()

pop메서드는 요소를 뒤에서 삭제한다(1개). 코드를 통해서 알아보자

const myArr = ['a', 'b', 'c'];
console.log(myArr);
myArr.pop(); 
console.log(myArr);

코드결과

마지막 요소인 'c'가 삭제되었다. 여기서 주의할 점은 삭제하는 것은 맞지만 좀 더 자세히 말하자면 꺼내온다 라는 말이 좀 더 어울릴 거 같다. 다음 코드를 보자.

let myValue = '';
const myArr = ['a', 'b', 'c'];
console.log(myArr); // pop 하기전
myValue = myArr.pop(); // 변수안에 담아 보았다.
console.log(myArr); // pop 한 후
console.log('pop으로 꺼내온 값 : ' + myValue);

결과

이렇게 코드를 통해 보듯이 pop을 해서 배열안에 요소를 삭제하고 그 값을 이용할 수 있다.(꺼내온다 라고 해석하는게 좋을거같다.)

 

Array.prototype.unshift()

unshift 메서드는  push와 마찬가지로 배열 요소를 삽입하는 것이다. 하지만 위치가 다르다. 앞에서 들어간다. 코드를 보자

const myArr = ['a', 'b', 'c'];
console.log(myArr); // unshift 하기전
myArr.unshift('D');
console.log(myArr); // unshift 한 후

 

결과

요소가 추가 되었다. 하지만 결과는 push와 반대이다. 앞에서 들어간다.

Array.prototype.shift()

shift 메서드는 pop과 비슷하다. 하지만 위치가 다르다. 맨 앞에 있는 것을 삭제한다. 코드를 보자

const myArr = ['a', 'b', 'c'];
console.log(myArr); // shift 하기전
myArr.shift();
console.log(myArr); // shift 한 후

결과

보이듯이 앞에서 하나가 삭제가 된다. 이것도 pop과 마찬가지로 사실 삭제가 아니라 꺼내온다라는 개념이 알맞는거 같다. 아래에 코드를 보자

let myValue = '';
const myArr = ['a', 'b', 'c'];
console.log(myArr); // shift 하기 전
myValue = myArr.shift(); // shift한 것을 변수에 담았다.
console.log(myArr); // shift 한 후
console.log('shift 된 배열의 요소: '+myValue);

결과

이것도 pop과 마찬가지로 변수에 삭제된 배열의 요소가 담겨있다. 이렇듯 꺼내온다는 개념이 좀더 명확한거 같다.

 

Array.prototype.splice()

위에 설명한 메서드들은 맨 앞, 맨 뒤 에서 하나밖에 삭제, 추가 밖에 하지 못했다. 하지만 지금 공부할 splice 메서드는 중간에서 삽입할 수 있고 삭제도 할 수 있다.

 

splice( 몇번째 위치, 삭제할 개수, 삽입할 요소(생략가능)) 이런 형식으로 적으면 된다.

여기서 위치는 index값이다.

const myArr = ['a', 'b', 'c'];
console.log(myArr); // splice 전
myArr.splice(0, 1, 'D','F','E'); // index 2 에서 1개 삭제 후 D, F, E 삽입
console.log(myArr); // splice 후

결과

이렇듯 splice는 삽입, 삭제 둘다 할 수 있다. 물론 삭제만 하는 것도 가능 하다. 근데 이것도 삭제라기보다는 꺼내온다는 개념이다.

 

let myValue = null;
const myArr = ['a', 'b', 'c','d','e'];
console.log(myArr);
myValue = myArr.splice(0, 2, 'D'); // 0번 인덱스부터 2개 삭제 후 D 삽입
console.log(myArr);
console.log('splice로 꺼내온 값: '+myValue);
console.log(myValue); // 꺼내온 값 찍어보기

결과

이렇게 꺼내오게 된다. 2개를 삭제하니 배열에 그 2개의 값이 담겨 리턴이 되었다. 

 

Array.prototype.concat()

concat 메서드는 기존 배열을 건드리지 않고 새로운 배열을 만든다.

자료들은 뒤에서 들어가게된다.

위의 메서드들은 기존 배열을 삭제하고 삽입하고 했었지만 concat을 이용하면 새로운 배열을 만들어내고, 기존 배열을 건드리지 않게 된다. 코드를 보자

const myArr = ['a', 'b', 'c'];
console.log(myArr);
const myArr2 = myArr.concat('A','B');
console.log(myArr); // 기존 배열 찍기
console.log(myArr2); // 새로운 배열 찍기

결과

이상으로 배열 메서드 관련 포스팅을 마칩니다.

 

부족한 점이 많아 죄송합니다. 지적할 부분이 있으면 알려주시면 수정하겠습니다.

반응형
반응형

클로저(closure)란?

클로저는 외부함수가 return으로 죽었음에도 불구하고 내부함수에서 외부함수에 위치한 변수의 제어가 가능한 경우이다.

 

왜 사용하는 것일까?

클로저를 공부하기 전에 제일 중요한건 왜? 도대체 왜 사용하는 것인지에 대해서 알아야한다.

아래에 코드를 보자.

function outer() {
  	let myNumber = 1;
  	function inner() {
      	 	document.write(++myNumber + '<br>');
  	}
       inner();
}
outer();
outer();
outer();
outer();

위 코드의 결과

위의 코드를 보면 outer 함수를 Call 하면 inner함수가 실행 되고 화면에 myNumber를 찍게 된다.

이것을 이해하기에는 문제가 없을 것이다.

그런데 나는 저 안에 들어있는 myNumber 변수가 계속해서 초기화 되지 않게 하고 싶다. 처음 한번만 초기화 되고 그 다음부터는 계속해서 누적이 되게 만들고 싶다.

그럼 다음 코드를 보자.

function outer() {
  	let myNumber = 1;
  	function inner() {
      	 	document.write(++myNumber + '<br>');
  	}
    return inner;
}
let myFunc = outer();
myFunc();
myFunc();
myFunc();
myFunc();

 

결과

myFunc 에는 inner함수 자체가 들어 있다. 그렇기 때문에 myFunc(); 로 myFunc를 Call 하면

inner 함수내부가 실행되게 된다.

여기서 중요하다.

myFunc() 이렇게 call할때마다 inner함수 내부에 있는 구문이 실행이 되는데 myNumber 라는 변수가 1씩 계속 증가하는 결과를 보인다.

 

함수는 return을 하면 그 함수는 종료가 되는 것을 안다. 

그런데 outer함수는 return inner;를 하고  종료가 되었음에도 우리는 myNumber 변수를 제어할 수 있다.

 

이것이 클로저이다.

 

클로저는 Lexical Scope에 영향받지 않는 Static 변수를 만들어내기 위한 방법이다.

Lexical Scope는 그냥 간단하게 말해  함수를 함수내부에 적으면 지역함수가 되고 함수내부에 변수는 지역변수가 되듯이 그때 그때의 Scope를 말하는 것이다.

 

Static 변수는 다른 언어에는 있는데 javascript는 없다. 그래서 클로저를 이용해서 만들어 낸다.

이 변수는 1번 초기화되고 다음부터는 초기화 되지 않는 특성을 지니고 있다.

 

클로저를 쓰는 이유

렉시컬 스코프에 한정되지 않는 Static 변수를 만들어내기 위해서 클로저의 개념을 빌려 만들어낸다. 라고 나는 해석했다. 또한, 클로저는 자바스크립트에만 있는 개념이 아니라 다른언어에도 있는 개념이다.

 

 

잘못된 부분이 있다면 지적해주시면 감사하겠습니다

반응형

+ Recent posts