Friday, February 10, 2012

jQuery 에서 동적 이벤트 등록시 이벤트 중복 문제

Javascript 에서 addEventListener 그리고 jQuery에서 .bind , .click 등 으로 다양하게 이벤트를 등록할 수 있다. 하지만, 어떤 DOM에 이벤트가 등록되는지에 따라 이벤트는 원하는 대로 동작하지 않는 경우가 발생할 수 있다. 몇가지 상황을 들어 본다.

1. 동적으로 생성되는 DOM에 대한 이벤트 등록
동적으로 새롭게 생성되는 이벤트에 대한 등록은 상대적으로 안전하다고 본다. 생성되는 이벤트에 대해 이벤트가 1:1로 등록이 되게 코딩을 한다는 가정 하에서. 하지만, DOM이 여러번 생성되고 동적 생성시 동적 삭제가 제대로 동반되지 않을 때 문제가 발생할 수 있다.

$('#create').click( function(){
     $('<div id=button >Button</div>').appendTo('body');
     $('#button').click( function(){
          alert('good');
     });
  });

위의 코드는 어떻게 동작할까? create 을 누르면 id=button인 버튼이 생성이 되고, 버튼을 누르면 'good' 이란 문구를 띄우는 창이 발생하는 이벤트가 등록된다. create를 두번 누르면 어떻게 될까?

버튼이 하나더 생긴다! 하지만, 새로 만들어진 버튼에는 이벤트가 등록되지 않는다. 동시에 처음에 만들어졌던 버튼을 누르면 alert 창이 두번 뜬다. id=button를 가진 첫번째 엘리먼트에 이벤트가 등록되므로 이후로 만들어지는 버튼에는 이벤트가 등록되지 않는다.

id 가 같은 엘리먼트가 여러개 만들어지는 건 피해야하지만, 그럼에도 불구하고 굳이 이 상황을 해결하고 싶다면, 즉 만들어지는 버튼 각각에 대해 이벤트를 등록하고 싶다면? 아래와 같이 각 엘리먼트 발생시 직접 이벤트를 연결해주면 된다.

$('#create').click( function(){
     $('<div id=button>Button</div>').appendTo('body').click( function(){
          alert('good');
     });
  });

2. 존재하는 DOM에 대한 이벤트 등록
보통 문제는 존재하는 DOM에 대한 이벤트 등록이다.
$('#button').click( function(){
       $('#start').click( function(){
               alert('What?');
       });
  });

위의 코드는 버튼( id=button인 엘리먼트)을 클릭하면, id=start 인 엘리먼트에 alert 를 띄우는 이벤트를 등록한다. 버튼을 한번 누르고 나면 이후에 start 를 눌렀을 때, 한번 alert 창을 띄운다, 이것이 이 코드를 작성한 사람의 의도일 것이다.

그런데 버튼을 두번 누르면 어떻게 될까?

start 엘리먼트에 이벤트가 두번 등록된다. 그래서 start 를 누르면 두번 alert 창이 뜬다. 한번 더 버튼을 누르면? 당연히 세번 alert 창이 뜬다.
이 문제를 해결하는 가장 간단한 방법은 버튼을 누를 때 마다 아래와 같이 기존에 등록된 'click' 이벤트를 제거하는 것이다.

$('#button').click( function(){
       $('#start').unbind('click');
       $('#start').click( function(){
               alert('What?');
       });
  });

기본적으로 동적 생성되는 DOM 이 아닌 경우라면 이벤트 등록을 중복하지 않는 것이 후폭풍을 막는 지름길이 아닐까 생각하고, 써야하는 상황이라면 항상 이벤트 중복이 되지 않게 주의를 기울여야 함을 기억해 놓자!!

5 comments:

  1. 코드에 대한 설정이 잘못되어 재발행 하였습니다. namo 님의 댓글이 날아간 것 같아 죄송합니다. 의견은 jQuery 1.3부터 동적 생성 DOM에 대한 이벤트 발행시 .live() 함수를 쓰면 더 좋은 해결법이 된다는 것이었습니다.

    ReplyDelete
  2. namo 님의 의견에 대한 답변: 1) .live() 함수를 쓰는 것과 글에 언급된 중복 이벤트 등록 문제는 하등의 관계가 없습니다. live()함수를 써서 이벤트 등록을 해도 이 문제는 여전히 발생하죠. 2) .live() 함수는 jQuery1.7 버전부터 on() 함수로 대체가 되고, 1.7 이하 버전에서는 live() 대신 delegate()을 쓰는 것을 권장하고 있습니다.

    ReplyDelete
  3. 동적으로 이벤트를 생성하는 부분이 있는데 자꾸 중복이 되서 왜 그런가 싶었는데 이제야 이유를 알았습니다! 정말 많은 도움이 되었습니다 : ]

    ReplyDelete
  4. 제가 JQuery로 간단한 게임을 만들고 있는데 게임이 끝나고 다시하기 버튼을 누르면 이상하게 되는 이유가 여기 있었네요.
    정말 많은 도움이 되었습니다! 감사합니다~

    ReplyDelete