모든 jquery 이벤트가 $ (document)에 바인딩되어야합니까?


164

이것이 어디에서 오는가

jQuery를 처음 배울 때 일반적으로 다음과 같은 이벤트를 첨부했습니다.

$('.my-widget a').click(function() {
    $(this).toggleClass('active');
});

선택기 속도 및 이벤트 위임에 대해 자세히 알고 난 후 "jQuery 이벤트 위임으로 코드를 더 빠르게 만들 수 있습니다."라는 내용을 여러 곳에서 읽었습니다. 그래서 다음과 같은 코드를 작성하기 시작했습니다.

$('.my-widget').on('click','a',function() {
    $(this).toggleClass('active');
});

사용되지 않는 .live () 이벤트의 동작을 복제하는 데 권장되는 방법이기도합니다. 많은 내 사이트가 항상 동적으로 위젯을 추가 / 제거하기 때문에 나에게 중요합니다. 그러나 기존 컨테이너 '.my-widget'에 추가 된 요소 만 동작을 가져 오기 때문에 위의 내용은 .live ()와 똑같이 동작하지 않습니다. 해당 코드가 실행 된 후 다른 html 블록을 동적으로 추가하면 해당 요소에 이벤트가 바인딩되지 않습니다. 이처럼 :

setTimeout(function() {
    $('body').append('<div class="my-widget"><a>Click does nothing</a></div>');
}, 1000);


내가 달성하고 싶은 것 :

  1. .live ()의 이전 동작 // 아직 존재하지 않는 요소에 이벤트를 첨부하는 것을 의미
  2. .on ()의 장점
  3. 이벤트를 바인딩하는 가장 빠른 성능
  4. 이벤트를 관리하는 간단한 방법

이제 다음과 같은 모든 이벤트를 첨부합니다.

$(document).on('click.my-widget-namespace', '.my-widget a', function() {
    $(this).toggleClass('active');
});

내 목표를 모두 충족시키는 것 같습니다. (예, IE에서 속도가 느리며 이유가 무엇인지 모릅니다) 단일 이벤트 만 단일 요소에 연결되고 보조 선택기는 이벤트가 발생할 때만 평가되므로 빠릅니다 (여기서 잘못된 경우 수정하십시오). 네임 스페이스는 이벤트 리스너를 더 쉽게 전환 할 수 있기 때문에 훌륭합니다.

내 솔루션 / 질문

따라서 jQuery 이벤트는 항상 $ (document)에 바인딩되어야한다고 생각하기 시작했습니다.
이것을 원하지 않는 이유가 있습니까?
이것이 모범 사례로 간주 될 수 있습니까? 그렇지 않다면 왜?

이 모든 것을 읽었다면 감사합니다. 모든 의견 / 통찰에 감사드립니다.

가정 :

  1. .on()// 버전 1.7 이상 을 지원하는 jQuery 사용
  2. 동적으로 추가 된 컨텐츠에 이벤트를 추가하려고합니다.

읽기 / 예 :

  1. http://24ways.org/2011/your-jquery-now-with-less-suck
  2. http://brandonaaron.net/blog/2010/03/4/event-delegation-with-jquery
  3. http://www.jasonbuckboyer.com/playground/speed/speed.html
  4. http://api.jquery.com/on/

답변:


215

아니요-위임 된 모든 이벤트 처리기를 document개체에 바인딩해서는 안됩니다 . 아마도 당신이 만들 수있는 최악의 시나리오 일 것입니다.

먼저, 이벤트 위임이 항상 코드를 더 빨리 만드는 것은 아닙니다. 어떤 경우에는 유리하며 어떤 경우에는 그렇지 않습니다. 실제로 이벤트 위임이 필요할 때와 그로부터 혜택을받을 때 이벤트 위임을 사용해야합니다. 그렇지 않으면 이벤트 처리기가 일반적으로 더 효율적이므로 이벤트 처리기가 이벤트가 발생하는 개체에 직접 바인딩되어야합니다.

둘째, 문서 수준에서 모든 위임 된 이벤트를 바인딩해서는 안됩니다. 이 .live()방법으로 묶인 이벤트가 많을 때 매우 비효율적이기 때문에 이것이 사용되지 않는 이유입니다. 위임 된 이벤트 처리의 경우 동적이 아닌 가장 가까운 상위에 바인딩하는 것이 훨씬 효율적입니다.

셋째, 위임으로 모든 이벤트가 작동하거나 모든 문제를 해결할 수있는 것은 아닙니다. 예를 들어, 입력 컨트롤에서 키 이벤트를 가로 채고 유효하지 않은 키가 입력 컨트롤에 입력되는 것을 차단하려면 이벤트가 위임 된 처리기로 버블 링 될 때까지 이벤트가 위임 된 이벤트 처리를 통해이를 수행 할 수 없습니다. 입력 컨트롤에 의해 처리되었으며 해당 동작에 영향을주기에는 너무 늦습니다.

이벤트 위임이 필요하거나 유리한 경우는 다음과 같습니다.

  • 이벤트를 캡처하는 오브젝트가 동적으로 작성 / 제거되는 경우 새 오브젝트를 작성할 때마다 이벤트 핸들러를 명시 적으로 리 바인드하지 않고도 해당 이벤트를 캡처하려고 할 때.
  • 모두 정확히 동일한 이벤트 처리기를 원하는 많은 객체가있는 경우 (로트가 수백 이상인 경우). 이 경우 설정시 수백 개 이상의 직접 이벤트 처리기보다 하나의 위임 된 이벤트 처리기를 바인딩하는 것이 더 효율적일 수 있습니다. 위임 된 이벤트 처리는 직접 이벤트 처리기보다 런타임시 항상 효율적이지 않습니다.
  • 문서의 모든 요소에서 발생하는 이벤트를 문서의 상위 레벨에서 캡처하려고 할 때.
  • 디자인에서 명시 적으로 이벤트 버블 링 및 stopPropagation ()을 사용하여 페이지의 일부 문제 또는 기능을 해결하는 경우

이것을 조금 더 이해하려면 jQuery 위임 이벤트 핸들러의 작동 방식을 이해해야합니다. 이런 식으로 전화하면 :

$("#myParent").on('click', 'button.actionButton', myFn);

#myParent객체 에 일반 jQuery 이벤트 핸들러를 설치 합니다. click 이벤트가이 위임 된 이벤트 처리기로 버블 링되면 jQuery는이 객체에 연결된 위임 된 이벤트 처리기 목록을 살펴보고 이벤트의 시작 요소가 위임 된 이벤트 처리기의 선택기와 일치하는지 확인해야합니다.

선택기가 상당히 관여 할 수 있기 때문에 jQuery는 각 선택기를 구문 분석 한 다음이를 원래 이벤트 대상의 특성과 비교하여 각 선택기와 일치하는지 확인해야합니다. 이것은 저렴한 작업이 아닙니다. 둘 중 하나만 있으면 큰 문제는 아니지만 모든 선택기를 문서 객체에 배치하고 모든 단일 버블 이벤트와 비교할 수백 개의 선택기가 있으면 이벤트 처리 성능이 크게 저하 될 수 있습니다.

이러한 이유로, 위임 된 이벤트 핸들러가 대상 오브젝트에 최대한 가깝도록 위임 된 이벤트 핸들러를 설정하려고합니다. 이는 각 위임 된 이벤트 핸들러를 통해 버블 링되는 이벤트 수가 줄어들어 성능이 향상됨을 의미합니다. 모든 버블 링 된 이벤트는 모든 위임 된 이벤트 처리기를 통과하고 가능한 모든 위임 된 이벤트 선택기에 대해 평가되어야하므로 문서 객체에 모든 위임 된 이벤트를 배치하는 것은 최악의 성능입니다. 이것이 바로 왜 .live()사용되지 않으며 .live()매우 비효율적 인 것으로 판명 되었기 때문에 이것이 사용되지 않는 이유 입니다 .


따라서 최적화 된 성능을 달성하려면 다음을 수행하십시오.

  1. 실제로 필요한 기능을 제공하거나 성능을 향상시킬 때 위임 된 이벤트 처리 만 사용하십시오. 실제로 필요하지 않기 때문에 쉽게 사용하기 때문에 항상 사용하지 마십시오. 실제로 이벤트 디스패치시 직접 이벤트 바인딩보다 성능이 떨어집니다.
  2. 위임 된 이벤트 핸들러를 가능한 가장 가까운 상위에 이벤트 소스에 첨부하십시오. 이벤트를 캡처하려는 동적 요소가있어 위임 된 이벤트 처리를 사용하는 경우 동적이 아닌 가장 가까운 상위를 선택하십시오.
  3. 위임 된 이벤트 핸들러에 대해 평가하기 쉬운 선택기를 사용하십시오. 위임 된 이벤트 처리의 작동 방식을 따랐다면, 위임 된 이벤트 핸들러는 많은 오브젝트와 많은 시간을 비교해야하므로 가능한 한 효율적으로 선택기를 선택하거나 오브젝트에 간단한 클래스를 추가하여 더 간단한 선택기를 사용할 수 있습니다. 위임 된 이벤트 처리 성능을 향상시킵니다.

2
대박. 빠르고 자세한 설명을 주셔서 감사합니다. .on ()을 사용하여 이벤트 디스패치 시간이 증가한다는 것은 의미가 있지만 이벤트 디스패치 시간을 초기 페이지 처리 팀과 비교하여 결정하는 데 여전히 어려움을 겪고 있습니다. 일반적으로 초기 페이지 시간이 단축되었습니다. 예를 들어, 페이지에 200 개의 요소가 있고 이벤트가 발생하면 더 많은 이벤트가 발생하는 경우 초기 이벤트에서 100 개의 이벤트 리스너를 추가해야하기 때문에 초기로드시 약 100 배 더 비쌉니다. 상위 컨테이너 링크
Buck

또한 당신이 저를 설득하고 싶다고 말하고 싶었습니다-모든 이벤트를 $ (문서)에 묶지 마십시오. 변하지 않는 가장 가까운 부모에게 가능한 한 바인딩합니다. 이벤트 위임이 이벤트를 요소에 직접 바인딩하는 것보다 낫다면 여전히 사례별로 결정해야 할 것 같습니다. 그리고 변경되지 않는 부모가없는 동적 콘텐츠와 관련된 이벤트가 필요할 때 @Vega가 아래에서 권장하는 작업을 계속 수행하고 내용이 삽입 된 후 핸들러를 바인딩합니다. ) DOM ', 선택기에서 jQuery의 컨텍스트 매개 변수를 사용합니다.
Buck

5
@ user1394692-거의 동일한 요소가 많으면 위임 된 이벤트 핸들러를 사용하는 것이 좋습니다. 나는 내 대답으로 그렇게 말합니다. 거대한 500 개의 행 테이블이 있고 특정 열의 모든 행에 동일한 단추가있는 경우 테이블에서 하나의 위임 된 이벤트 핸들러를 사용하여 모든 단추를 제공합니다. 그러나 200 개의 요소가 있고 모두 고유 한 고유 한 이벤트 처리기가 필요한 경우 200 개의 위임 된 이벤트 처리기를 설치하는 것이 200 개의 직접 바인딩 된 이벤트 처리기를 설치하는 것보다 빠르지는 않지만 위임 된 이벤트 처리는 이벤트 실행시 훨씬 느려질 수 있습니다.
jfriend00

당신은 완벽합니다. 전적으로 동의합니다! 이것이 내가 할 수있는 최악의 일이라는 것을 알고 $(document).on('click', '[myCustomAttr]', function () { ... });있습니까?
Artem Fitiskin 14

pjax / turbolinks를 사용하면 모든 요소가 동적으로 생성 / 제거되기 때문에 (첫 페이지로드 제외) 흥미로운 문제가 발생합니다. 그래서 더 한 번 모든 이벤트를 결합하는 것입니다 document, 또는 바인드에보다 구체적인 선택에 page:load에 풀릴 이러한 이벤트 page:after-remove? 흠
돔 크리스티

9

이벤트 위임은 요소가 실제로 DOM에 존재하기 전에 핸들러를 작성하는 기술입니다. 이 방법에는 고유 한 단점이 있으며 이러한 요구 사항이있는 경우에만 사용해야합니다.

이벤트 위임은 언제 사용해야합니까?

  1. 동일한 기능이 필요한 더 많은 요소에 대해 공통 핸들러를 바인딩하는 경우 (예 : 테이블 행 호버)
    • 이 예에서 직접 바인드를 사용하여 모든 행을 바인드해야하는 경우 해당 테이블에서 n 개의 행에 대해 n 핸들러를 작성하게됩니다. 위임 방법을 사용하면 하나의 간단한 처리기에서 모든 것을 처리 할 수 ​​있습니다.
  2. DOM에서 동적 내용을 더 자주 추가하는 경우 (예 : 테이블에서 행 추가 / 제거)

왜 이벤트 위임을 사용하지 않아야합니까?

  1. 이벤트를 요소에 직접 바인딩하는 것과 비교할 때 이벤트 위임이 느려집니다.
    • 적중 할 때마다 대상 셀렉터를 비교합니다. 비교는 복잡한만큼 비용이 많이 듭니다.
  2. 바인드 된 요소에 도달 할 때까지 이벤트 버블 링을 제어 할 수 없습니다.

추신 : 동적 내용의 경우에도 내용이 DOM에 삽입 된 후 핸들러를 바인딩하면 이벤트 위임 메소드를 사용할 필요가 없습니다. (동적 컨텐츠를 추가하거나 자주 추가하지 않는 경우)



0

jfriend00의 답변에 몇 가지 언급과 반론을 추가하고 싶습니다. (주로 내 직감에 근거한 나의 의견)

아니오-위임 된 모든 이벤트 핸들러를 문서 오브젝트에 바인드해서는 안됩니다. 아마도 당신이 만들 수있는 최악의 시나리오 일 것입니다.

먼저, 이벤트 위임이 항상 코드를 더 빨리 만드는 것은 아닙니다. 어떤 경우에는 유리하며 어떤 경우에는 그렇지 않습니다. 실제로 이벤트 위임이 필요할 때와 그로부터 혜택을받을 때 이벤트 위임을 사용해야합니다. 그렇지 않으면 이벤트 처리기가 일반적으로 더 효율적이므로 이벤트 처리기가 이벤트가 발생하는 개체에 직접 바인딩되어야합니다.

단일 요소에 대해서만 등록하고 이벤트를 진행할 경우 성능이 약간 향상 될 수는 있지만, 위임이 가져 오는 확장 성 이점과 비교할 필요는 없습니다. 나는 이것에 대한 증거가 없지만 브라우저가 이것을보다 효율적으로 처리 할 것이라고 믿습니다. 제 생각에, 이벤트 위임은 갈 길입니다!

둘째, 모든 위임 된 이벤트를 문서 수준에서 바인딩해서는 안됩니다. 이것이 바로 .live ()가 더 이상 사용되지 않는 이유입니다.이 방법으로 묶인 이벤트가 많을 때 매우 비효율적입니다. 위임 된 이벤트 처리의 경우 동적이 아닌 가장 가까운 상위에 바인딩하는 것이 훨씬 효율적입니다.

나는 이것에 동의합니다. 이벤트가 컨테이너 내부에서만 발생한다고 100 % 확신하는 경우 이벤트를이 컨테이너에 바인딩하는 것이 합리적이지만 트리거 요소에 이벤트를 직접 바인딩하는 것에 대해서는 여전히 논쟁의 여지가 있습니다.

셋째, 위임으로 모든 이벤트가 작동하거나 모든 문제를 해결할 수있는 것은 아닙니다. 예를 들어, 입력 컨트롤에서 키 이벤트를 가로 채고 유효하지 않은 키가 입력 컨트롤에 입력되는 것을 차단하려면 이벤트가 위임 된 처리기로 버블 링 될 때까지 이벤트가 위임 된 이벤트 처리를 통해이를 수행 할 수 없습니다. 입력 컨트롤에 의해 처리되었으며 해당 동작에 영향을주기에는 너무 늦습니다.

이것은 단순히 사실이 아닙니다. 이 코드를 참조하십시오 : 펜 : https://codepen.io/pwkip/pen/jObGmjq

document.addEventListener('keypress', (e) => {
    e.preventDefault();
});

문서에 키 누르기 이벤트를 등록하여 사용자가 입력하지 못하게하는 방법을 보여줍니다.

이벤트 위임이 필요하거나 유리한 경우는 다음과 같습니다.

이벤트를 캡처하는 오브젝트가 동적으로 작성 / 제거되는 경우 새 오브젝트를 작성할 때마다 이벤트 핸들러를 명시 적으로 리 바인드하지 않고 이벤트를 캡처하려고 할 때. 모두 정확히 동일한 이벤트 처리기를 원하는 많은 객체가있는 경우 (로트가 수백 이상인 경우). 이 경우 설정시 수백 개 이상의 직접 이벤트 핸들러가 아닌 하나의 위임 된 이벤트 핸들러를 바인드하는 것이 더 효율적일 수 있습니다. 위임 된 이벤트 처리는 직접 이벤트 처리기보다 런타임시 항상 효율적이지 않습니다.

https://ehsangazar.com/optimizing-javascript-event-listeners-for-performance-e28406ad406c 에서이 인용문으로 회신하고 싶습니다.

Event delegation promotes binding as few DOM event handlers as possible, since each event handler requires memory. For example, let’s say that we have an HTML unordered list we want to bind event handlers to. Instead of binding a click event handler for each list item (which may be hundreds for all we know), we bind one click handler to the parent unordered list itself.

또한 인터넷 검색을 사용 performance cost of event delegation google하면 이벤트 위임을 위해 더 많은 결과를 얻을 수 있습니다.

문서의 모든 요소에서 발생하는 이벤트를 문서의 상위 레벨에서 캡처하려고 할 때. 디자인에서 명시 적으로 이벤트 버블 링 및 stopPropagation ()을 사용하여 페이지의 일부 문제 또는 기능을 해결하는 경우 이것을 조금 더 이해하려면 jQuery 위임 이벤트 핸들러의 작동 방식을 이해해야합니다. 이런 식으로 전화하면 :

$ ( "# myParent"). on ( 'click', 'button.actionButton', myFn); #myParent 객체에 일반 jQuery 이벤트 핸들러를 설치합니다. click 이벤트가이 위임 된 이벤트 처리기로 버블 링되면 jQuery는이 객체에 연결된 위임 된 이벤트 처리기 목록을 살펴보고 이벤트의 시작 요소가 위임 된 이벤트 처리기의 선택기와 일치하는지 확인해야합니다.

선택기가 상당히 관여 할 수 있기 때문에 jQuery는 각 선택기를 구문 분석 한 다음이를 원래 이벤트 대상의 특성과 비교하여 각 선택기와 일치하는지 확인해야합니다. 이것은 저렴한 작업이 아닙니다. 둘 중 하나만 있으면 큰 문제는 아니지만 모든 선택기를 문서 객체에 배치하고 모든 단일 버블 이벤트와 비교할 수백 개의 선택기가 있으면 이벤트 처리 성능이 크게 저하 될 수 있습니다.

이러한 이유로, 위임 된 이벤트 핸들러가 대상 오브젝트에 최대한 가깝도록 위임 된 이벤트 핸들러를 설정하려고합니다. 이는 각 위임 된 이벤트 핸들러를 통해 버블 링되는 이벤트 수가 줄어들어 성능이 향상됨을 의미합니다. 모든 버블 링 된 이벤트는 모든 위임 된 이벤트 처리기를 통과하고 가능한 모든 위임 된 이벤트 선택기에 대해 평가되어야하기 때문에 문서 객체에 모든 위임 된 이벤트를 배치하는 것은 최악의 성능입니다. 이것이 바로 .live ()가 더 이상 사용되지 않는 이유입니다. 이것이 .live ()가 수행 한 것이므로 매우 비효율적 인 것으로 판명되었습니다.

이것은 어디에 문서화되어 있습니까? 이것이 사실이라면 jQuery는 위임을 매우 비효율적 인 방식으로 처리하는 것으로 보이며 내 반론은 바닐라 JS에만 적용해야합니다.

여전히 :이 주장을 뒷받침하는 공식 출처를 찾고 싶습니다.

:: 편집하다 ::

jQuery가 실제로 매우 비효율적 인 방식으로 이벤트 버블 링을 수행하는 것처럼 보입니다 (IE8을 지원하기 때문에) https://api.jquery.com/on/#event-performance

따라서 여기의 대부분의 논쟁은 바닐라 JS와 최신 브라우저에만 적용됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.