[jQuery] .on()

참고 문서

.on()

하나 혹은 그 이상의 요소(노드)에 이벤트 핸들러를 부착(?)한다. 1

.on( events [, selector] [, data], handler )
  • events: 공백으로 구분된 하나 이상의 이벤트 타입과 옵션인 네임스페이스. 'click', 'keydown.myPlugin', '.myPlugin' 등이 있음
  • selector: 이벤트가 발생한 요소의 하위 요소를 의미하는 선택자. 이 값이 null이거나 생략하면 선택된 요소에 한해서 반응한다.
  • data: 이벤트가 발생할 때 핸들러에 전달할 데이터
  • handler: 이벤트가 발생되면 실행할 함수. 첫 번째 인자로 Event 객체가 전달된다. false를 반환하는 함수라면 간단하게 false를 명시하면 된다.

on()은 jQuery 1.7부터 bind(), delegate(), live()를 대체하는 메서드로 이벤트 핸들러 바인딩에 필요한 기존 메서드의 모든 기능을 제공한다. jQuery는 앞의 세 메서드 대신 on()을 사용할 것을 권장하고 있다.

$(선택자).on(이벤트타입 [, 하위선택자] [, 데이터], 핸들러())

on()은 기본적으로 jQuery 객체를 반환하지만 메서드의 전달인자로 추가적인 선택자를 명시하면 최초 요소의 하위를 재탐색하여 반환한다:

$("#btn_close").on("click", function() {
    self.close();
});

$("#btn").bind("click", function() {});
$("#btn").on("click", function() {});

$("body").delegate("#btn", "click", function() {});
$("body").on("click", "#btn", function() {});

$("#btn").live("click", function() {});
$("#btn").on("click", function() {});

보다시피 사용방법은 bind(), delegate(), live()와 큰 차이가 없는데, 주의해야 할 것은 on을 사용한다고 해서 무조건 동적처리가 가능한것은 아니라는 점. 여기서 동적 처리란 HTML 문서의 로딩이 완료된 후 스크립트에 의해 HTML 구성요소가 추가로 생성되었을 때, 이 구성요소에서 발생하는 이벤트를 감지하여 핸들러를 호출하는 과정을 의미한다.

<script>
  jQuery(document).ready(function() {
    $("#demo1 li").on("click", function() {
      alert($(this).text());
    });

    $("#demo1 button").on("click", function() {
      $("#demo1 ul").append("<li>newone</li>");
    });
  });
</script>
<div id="demo1">
    <button>ADD</button>
    <ul>
        <li>test1</li>
        <li>test2</li>
        <li>test3</li>
    </ul>
</div>
  • test1
  • test2
  • test3

위의 버튼들을 눌러볼 것. ADD에 의해 추가된 요소는 클릭 이벤트가 발생해도 아무런 반응이 없다.

반면 다음의 경우:

<script>
  jQuery(document).ready(function() {
    $("#demo2").on("click", "li", function() {
      alert($(this).text());
    });

    $("#demo2 button").on("click", function() {
      $("#demo2 ul").append("<li>newone</li>");
    });
  });
</script>
<div id="demo2">
    <button>ADD</button>
    <ul>
        <li>test1</li>
        <li>test2</li>
        <li>test3</li>
    </ul>
</div>
  • test1
  • test2
  • test3

ADD와 test#를 눌러보면 ADD에 의해 추가된 요소도 클릭 이벤트에 반응하는 것을 확인할 수 있다.

$(선택자).on(이벤트타입 [, 추가선택자] [, 데이터], 핸들러());
  1. 인자에 추가선택자가 없다 = bind()와 동일, 동적처리 불가능
  2. 인자에 추가선택자가 있다 = delegate()와 동일, 동적처리 가능

추가선택자를 명시하면 하위 요소에서 발생한 이벤트에도 반응하도록 핸들러를 등록한다. 이벤트가 발생한 노드가 추가선택자와 일치하면 핸들러가 작동하는 방식이다. 이벤트 버블링*을 이용한 방식으로 순수 자바스크립트에서도 구현할 수 있지만 jQuery에선 버블링을 지원하지 않는 브라우저를 위한 호환처리가 되어있다. (1.x 기준)

버블링이란?

계층적 구조의 포함되어 있는 특정 엘리먼트에 이벤트가 발생할 경우, 연쇄적인 반응이 일어나는데 이를 이벤트 전파라고 한다. 이벤트 대상 엘리먼트를 시작으로 최하위 엘리먼트까지 이벤트가 전파가 되는 것을 캡쳐링(Capturing)이라고 하고, 반대로 최상위 엘리먼트에서 대상 엘리먼트까지 이벤트가 전파되는 것을 버블링(Bubbling)이라 한다.

http://catcode.com/domcontent/events/capture.html

events

on() 메서드의 첫 번째 인자는 events는 공백으로 구분되는 문자열, 혹은 Object, map이 올 수 있다. 여기서 map은 생략한다.

공백으로 구분된 문자열

'공백으로 구분되는 문자열'이란 "eventType1 eventType2 eventType3…"와 같은 형태를 말한다.

실제로 사용해보자:

<script>
  jQuery(document).ready(function() {
    $("#demo3 button").on("click mouseout", function() {
      $("#demo3 #result").html("<span style='color: red;'>something happened</span>");
      $("#demo3 #result span").show(0, function() {
        $(this).fadeOut(1000);
      });
      // $("#demo3 #result span").fadeOut(1000);
    });
  });
</script>
<div id="demo3">
    <button>눌러보세요</button>
    <div id="result"></div>
</div>

Object

.on( events [, selector ] [, data ] )

Object는 아래처럼 사용한다:

<script>
  jQuery(document).ready(function() {
    $("#demo4 button").on({
      click: function() {
        $("#demo4 #result").html("<span style='color: red;'>click event</span>");
      },
      mouseover: function() {
        $("#demo4 #result").html("<span style='color: blue;'>mouseover event</span>");
      }
    });
  });
</script>
<div id="demo4">
    <button>눌러보세요</button>
    <div id="result"></div>
</div>

문자열과 다르게 각각 다른 핸들러를 전달할 수 있다.

만약 버블링이 필요한 경우 다음처럼:

<script>
     jQuery(document).ready(function() {
        $("#demo6").on({
            click: function() {
                $("#demo6 #result").html("<span style='color: red;'>click event</span>")
            },
            mouseover: function() {
                $("#demo6 #result").html("<span style='color: blue;'>mouseover event</span>")
            }
        }, "div");

        $("#demo6 button").on("click", function() {
            $("#demo6").append("<div>추가된 놈</div>");
        });
    });
</script>
<div id="demo6">
    <button>ADD</button> <span id="result"></span>
    <div>원래 있던 놈</div>
</div>

Object 뒤에 selector를 추가로 명시한다.

 
원래 있던 놈

event namespaces

네임스페이스를 활용하면 하나의 이벤트에 다수의 핸들러를 할당하고 이를 컨트롤 해야 할 때 접근을 쉽게 할 수 있다. 예를 들어 button태그의 click 이벤트 발생 시 한번에 네 개의 함수가 작동하도록 만든다고 하자. 이를 네임스페이스로 바인딩했다면 차후 동적처리로 특정함수를 제거해야 한다고 할 때 해당 네임스페이스를 이벤트타입으로 지정하여 삭제할 수 있다.

다음은 하나의 클릭 이벤트에 다수의 핸들러를 네임스페이스를 이용해 바인딩하고 그 중 일부만 삭제하는 예시다:

<script>
  jQuery(document).ready(function() {
    $("#demo7 #do").on("click.first", function() {
      alert("first action");
    });

    $("#demo7 #do").on({
      "click.second": function() {
        alert("second action");
      },
      "click.third": function() {
        alert("third action");
      }
    });
  });

  function fn_del() {
    $("#demo7 button").off("click.second");
  }
</script>
<div id="demo7">
    <button id="do">ANG?</button>
    <button onclick="fn_del()">second action 삭제</button>
</div>

위에서 "click.first"는 click 이벤트의 first 네임스페이스를 의미하는데 여기서 네임스페이스는 1depth 이상일 수 있다. 이 말은 "click.action.event.first"라는 네임스페이스도 가능하다는 뜻이다.

data

on() 메서드의 인자로 null 이나 undefined 가 아닌 값이 지정되면 이 값은 event.data 속성을 통해 핸들러에 전달된다. data 인자는 어떤 타입이라도 사용할 수 있지만 만일 문자열을 사용했다면 데이터 선택 실수를 방지하기 위해 selector를 반드시 제공하거나 명시적으로 null을 지정해야 한다.

<script>
  jQuery(document).ready(function() {
    function foo(event) {
      alert(event.data.txt1 + event.data.txt2);
    }

    $("#demo8 #do").on("click", {txt1: "hello", txt2: " world!"}, foo);
  });
</script>
<div id="demo8">
    <button id="do">HI</button>
</div>

.bind()

.bind( eventType [, eventData] , handler( eventObject ) )
  • eventType: 공백으로 구분된 하나 이상의 JavaScript 이벤트를 포함하는 문자열, "click", "keydown" 또는 사용자 정의 함수명

bind()는 jQuery로 선택한 DOM 객체에 이벤트 핸들러를 등록하는 메서드다.

<script>
  jQuery(document).ready(function() {
    $("#demo9 li").bind("click", function() {
      alert($(this).text());
    });
  });
</script>
<div id="demo9">
    <ul>
        <li>test1</li>
        <li>test2</li>
        <li>test3</li>
    </ul>
</div>
  • test1
  • test2
  • test3

jQuery는 매우 빈번하게 일어나는 이벤트에 대해선 bind()를 사용하지 않고 사용할 수 있도록 일종의 전용 바인더를 제공하는데 jQuery.click(), jQuery.hover() 등이 그것이다. 그런데 문제는 이런 bind 계열 메서드가 동적으로 생성된 노드에 접근하도록 하는게 번거롭다는 점이다.

다음 예를 보면:

<script>
  jQuery(document).ready(function() {
    $("#demo10 button").bind("click", function() {
      $("#demo10 ul").append("<li>newone</li>");
    });

    $("#demo10 li").click(function() {
      alert($(this).text());
    });
  });
</script>
<div id="demo10">
    <button>add</button>
    <ul>
        <li>test1</li>
        <li>test2</li>
        <li>test3</li>
    </ul>
</div>
  • test1
  • test2
  • test3

add 버튼 클릭 시 동적으로 생성된 노드의 li 요소는 클릭해도 아무런 반응을 보이지 않는다. 이는 bind()에 전달된 jQuery 객체가 document.ready 시점에 생성된 노드로 한정되기 때문이다. 따라서 문서로딩 이후 추가되는 노드에 이벤트를 등록하고자 한다면 다음처럼 소스를 고쳐야 한다:

<script>
  jQuery(document).ready(function() {
    $("#demo11 button").bind("click", function() {
      $("#demo11 li").unbind("click");
      $("#demo11 ul").append("<li>newone</li>").find($("li")).click(function() {
        alert($(this).text());
      });
    });

    $("#demo11 li").bind("click", function() {
      alert($(this).text());
    });
  });
</script>
  • test1
  • test2
  • test3

이 외에 버블링을 이용하는 방법이 있다.

<script>
  jQuery(document).ready(function() {
    $("#demo11 button").bind("click", function() {
      $("#demo11 ul").append("<li>newone</li>");
    });

    $("#demo11").bind("click", function(e) {
      if (e.target.tagName == "LI") {
        alert($(e.target).text());
      }
    })
  });
</script>

.delegate()

.delegate( selector, eventType, handler )

eventType: 공백으로 구분된 하나 이상의 JavaScript 이벤트를 포함하는 문자열, "click", "keydown" 또는 사용자 정의 함수명 다음은 delegate()를 사용해 동적으로 생성된 노드를 핸들링하는 코드다:

<script>
  jQuery(document).ready(function() {
    $("#demo12 button").bind("click", function() {
      $("#demo12 ul").append("<li>newone</li>");
    });

    $("#demo12").delegate("li", "click", function() {
      alert($(this).text());
    });
  });
</script>
  • test1
  • test2
  • test3

.live()

.live( events, handler )
<script src="http://code.jquery.com/jquery-1.6.4.js"></script>
<script>
  jQuery(document).ready(function() {
    $("#demo13 button").bind("click", function() {
      $("#demo13 ul").append("<li>added</li>");
    });

    $("#demo13 li").live("click", function() {
      alert($(this).text());
    });
  });
</script>
<div id="demo13">
    <button>ADD</button>
    <ul>
        <li>test1</li>
        <li>test2</li>
        <li>test3</li>
    </ul>
</div>

데모는 생략

live() 메서드는 1.7 이후 사용이 완전히 금지(실제로 1.7.x 이상의 라이브러리에서는 없는 메서드라며 에러가 난다)되었기 때문에 부득이하게 1.6.4 라이브러리를 이용하여 작성했다.

  1. Attach an event handler function for one or more events to the selected elements.