jQuery와 바인딩 된 이벤트를 주문하는 방법


164

4 개의 스크립트 블록을 포함 할 수있는 페이지가있는 웹 응용 프로그램이 있다고 가정 해 보겠습니다. 내가 작성한 스크립트는 해당 블록 중 하나에서 발견 될 수 있지만 컨트롤러가 처리하는 스크립트는 알 수 없습니다.

일부 onclick이벤트를 버튼에 바인딩 하지만 때로는 예상하지 못한 순서로 실행되는 것을 알았습니다.

주문을 보장 할 수있는 방법이 있습니까, 아니면 과거에이 문제를 어떻게 처리 했습니까?


1
콜백 객체에 콜백을 추가하면 해당 콜백을 한 번에 실행할 수 있으며 추가 된 순서대로 실행됩니다. api.jquery.com/jQuery.Callbacks
Tyddlywink

답변:


28

나는 이런 종류의 과정을 일반화하기 위해 여러 해 동안 노력했지만, 제 경우에는 체인에서 첫 번째 이벤트 리스너의 순서에만 관심이있었습니다.

사용중인 경우 다음은 항상 다른 이벤트보다 먼저 트리거되는 이벤트 리스너를 바인딩하는 jQuery 플러그인입니다.

** jQuery 변경 사항과 함께 업데이트 된 인라인 (Toskan 덕분에) **

(function($) {
    $.fn.bindFirst = function(/*String*/ eventType, /*[Object])*/ eventData, /*Function*/ handler) {
        var indexOfDot = eventType.indexOf(".");
        var eventNameSpace = indexOfDot > 0 ? eventType.substring(indexOfDot) : "";

        eventType = indexOfDot > 0 ? eventType.substring(0, indexOfDot) : eventType;
        handler = handler == undefined ? eventData : handler;
        eventData = typeof eventData == "function" ? {} : eventData;

        return this.each(function() {
            var $this = $(this);
            var currentAttrListener = this["on" + eventType];

            if (currentAttrListener) {
                $this.bind(eventType, function(e) {
                    return currentAttrListener(e.originalEvent); 
                });

                this["on" + eventType] = null;
            }

            $this.bind(eventType + eventNameSpace, eventData, handler);

            var allEvents = $this.data("events") || $._data($this[0], "events");
            var typeEvents = allEvents[eventType];
            var newEvent = typeEvents.pop();
            typeEvents.unshift(newEvent);
        });
    };
})(jQuery);

참고 사항 :

  • 이것은 완전히 테스트되지 않았습니다.
  • 변경되지 않는 jQuery 프레임 워크의 내부에 의존합니다 (1.5.2로만 테스트).
  • 소스 요소의 속성이나 jQuery bind () 및 기타 관련 함수를 사용하지 않고 다른 방식으로 바인딩 된 이벤트 리스너 전에 반드시 트리거되지는 않습니다.

이는 jQuery를 통해 추가 된 이벤트에만 적용됩니다.
Grinn

1
이것은 사용하기 때문에 더 이상 작동하지 않습니다. this.data("events")여기를 참조하십시오 stackoverflow.com/questions/12214654/…
Toskan

4
jQuery 1.8에 대해 비슷한 기능이 여기에 업데이트되었습니다 : stackoverflow.com/a/2641047/850782
EpicVoyage

136

순서가 중요한 경우 다른 콜백에 의해 해당 이벤트가 트리거 될 때 자체 이벤트를 생성하고 콜백을 시작하여 바인딩 할 수 있습니다.

$('#mydiv').click(function(e) {
    // maniplate #mydiv ...
    $('#mydiv').trigger('mydiv-manipulated');
});

$('#mydiv').bind('mydiv-manipulated', function(e) {
    // do more stuff now that #mydiv has been manipulated
    return;
});

적어도 그런 것.


23
바인드 코드 이전에 정의 된 콜백에 대한 클릭 이벤트의 전파를 중지하려면 어떻게해야합니까?
vinilios

2
이것이 왜 받아 들여지지 않았는지 물어볼 수 있습니까? 현재 받아 들여진 대답은 내 대답을 가장 좋은 방법으로 인용하므로 혼란 스럽습니다. :-)
dowski 2016 년

mkoryak의 경우에는 작동하지만 동일한 이벤트가 여러 번 호출되는 경우에는 작동하지 않습니다. 단일 키 입력이 $ (window) .scroll ()을 여러 번 역순으로 트리거하는 비슷한 문제가 있습니다.
kxsong

37

모든 콜백이 항상 존재하고 서로 의존하는 것에 만족한다면 Dowski의 방법이 좋습니다.

콜백이 서로 독립적이기를 원한다면 버블 링을 활용하고 후속 이벤트를 부모 요소의 대리자로 첨부 할 수 있습니다. 부모 요소의 처리기는 요소의 처리기 다음에 트리거되어 문서까지 계속됩니다. 이것은 당신이 사용할 수있는 아주 좋은 event.stopPropagation(), event.preventDefault()등 핸들러를 생략하고 취소 작업을 취소합니다 취소합니다.

$( '#mybutton' ).click( function(e) { 
    // Do stuff first
} );

$( '#mybutton' ).click( function(e) { 
    // Do other stuff first
} );

$( document ).delegate( '#mybutton', 'click', function(e) {
    // Do stuff last
} );

또는이 방법이 마음에 들지 않으면 Nick Leaches bindLast 플러그인을 사용하여 이벤트가 마지막으로 바인딩되도록 할 수 있습니다 : https://github.com/nickyleach/jQuery.bindLast .

또는 jQuery 1.5를 사용하는 경우 새로운 Deferred 객체로 영리한 작업을 수행 할 수도 있습니다.


6
.delegate더 이상 사용되지 않으며 이제 사용해야합니다 .on,하지만 난 당신과 같은 문제가 도달 할 수있는 방법을 모른다on
eric.itzhak

플러그인은 더 이상 작동하지 않으므로 수정되어야합니다. stackoverflow.com/questions/12214654/…
Toskan

@ eric.itzhak .on ()을 이전 .delegate ()처럼 동작하게하려면 선택기를 제공하십시오. 자세한 내용은 여기 api.jquery.com/delegate
Sam Kauffman

버블 링을 활용하려면 이벤트가 위임 되지 않고 직접 진행 되어야한다고 생각합니다 . api.jquery.com/on/#direct-and-delegated-events
Ryne Everett

15

바운드 콜백이 호출되는 순서는 각 jQuery 객체의 이벤트 데이터에 의해 관리됩니다. 해당 데이터를 직접보고 조작 할 수있는 함수 (내가 아는 것)는 없으며 bind () 및 unbind () (또는 동등한 도우미 함수) 만 사용할 수 있습니다.

Dowski의 방법이 가장 좋습니다. "실제"이벤트에 바인딩 된 "첫 번째"콜백을 사용하여 정렬 된 일련의 사용자 지정 이벤트에 바인딩하도록 다양한 바인딩 된 콜백을 수정해야합니다. 그렇게하면 순서에 관계없이 시퀀스가 ​​올바른 방식으로 실행됩니다.

내가 볼 수있는 유일한 대안은 실제로 생각하고 싶지 않은 것입니다. 함수의 바인딩 구문이 바인딩되기 전에 알고 있다면 해당 함수를 모두 바인딩 해제 한 다음 다시 바인딩하십시오. 적절한 순서대로. 이제 코드가 중복되었으므로 문제가 발생했습니다.

jQuery를 사용하여 객체의 이벤트 데이터에서 바운드 이벤트의 순서를 간단하게 변경할 수는 있지만 가능하지 않은 jQuery 코어에 연결하는 코드를 작성하지 않고도 멋질 것입니다. 그리고 내가 생각하지 않은 것을 허용하면 의미가있을 수 있으므로 의도적으로 누락되었을 수 있습니다.


12
jquery로 요소에 바인딩 된 모든 이벤트를 보려면 var varEvents = $ .data (this, "events");
redsquare

9

jQuery 유니버스에서는 버전 1.8과 다르게 구현해야합니다. 다음 릴리스 노트는 jQuery 블로그에 있습니다.

.data ( "events") : jQuery는 이벤트 관련 데이터를 각 요소의 (기다리는) 이벤트라는 데이터 객체에 저장합니다. 이것은 내부 데이터 구조이므로 1.8에서는 사용자 데이터 네임 스페이스에서 제거되므로 동일한 이름의 항목과 충돌하지 않습니다. jQuery의 이벤트 데이터는 여전히 jQuery._data (element, "events")를 통해 액세스 할 수 있습니다

jQuery 유니버스에서 처리기가 실행될 순서를 완전히 제어 할 수 있습니다 . Ricoo는 이것을 위에서 지적합니다. 그의 대답이 그에게 많은 사랑을 얻은 것처럼 보이지는 않지만이 기술은 매우 유용합니다. 예를 들어, 라이브러리 위젯에서 일부 핸들러에 앞서 자체 핸들러를 실행해야하거나 조건부로 위젯 핸들러에 대한 호출을 취소 할 수있는 권한이 있어야하는 경우를 고려하십시오.

$("button").click(function(e){
    if(bSomeConditional)
       e.stopImmediatePropagation();//Don't execute the widget's handler
}).each(function () {
    var aClickListeners = $._data(this, "events").click;
    aClickListeners.reverse();
});

7

핸들러를 정상적으로 바인드하고 다음을 실행하십시오.

element.data('events').action.reverse();

예를 들어 :

$('#mydiv').data('events').click.reverse();

2
작동하지 않지만 작동 $._data(element, 'events')[name].reverse()합니다
Attenzione

7
function bindFirst(owner, event, handler) {
    owner.unbind(event, handler);
    owner.bind(event, handler);

    var events = owner.data('events')[event];
    events.unshift(events.pop());

    owner.data('events')[event] = events;
}

3

다음과 같이 시도해보십시오.

/**
  * Guarantee that a event handler allways be the last to execute
  * @param owner The jquery object with any others events handlers $(selector)
  * @param event The event descriptor like 'click'
  * @param handler The event handler to be executed allways at the end.
**/
function bindAtTheEnd(owner,event,handler){
    var aux=function(){owner.unbind(event,handler);owner.bind(event,handler);};
    bindAtTheStart(owner,event,aux,true);

}
/**
  * Bind a event handler at the start of all others events handlers.
  * @param owner Jquery object with any others events handlers $(selector);
  * @param event The event descriptor for example 'click';
  * @param handler The event handler to bind at the start.
  * @param one If the function only be executed once.
**/
function bindAtTheStart(owner,event,handler,one){
    var eventos,index;
    var handlers=new Array();
    owner.unbind(event,handler);
    eventos=owner.data("events")[event];
    for(index=0;index<eventos.length;index+=1){
        handlers[index]=eventos[index];
    }
    owner.unbind(event);
    if(one){
        owner.one(event,handler);
    }
    else{
        owner.bind(event,handler);
    }
    for(index=0;index<handlers.length;index+=1){
        owner.bind(event,ownerhandlers[index]);
    }   
}

"소유자"는 "핸들러"여야한다고 생각합니다.
Joril

1

나는 같은 문제가 있으며이 주제를 찾았습니다. 위의 답변으로 이러한 문제를 해결할 수 있지만 좋은 계획이라고 생각하지 않습니다.

우리는 현실 세계에 대해 생각합시다.

이러한 답변을 사용하려면 코드를 변경해야합니다. 코드 스타일을 변경해야합니다. 이 같은:

실물:

$('form').submit(handle);

마구 자르기:

bindAtTheStart($('form'),'submit',handle);

시간이 지남에 따라 프로젝트에 대해 생각하십시오. 코드는보기 흉하고 읽기 어렵다! 간단한 이유는 항상 더 좋습니다. bindAtTheStart가 10 개이면 버그가 없을 수 있습니다. bindAtTheStart가 100 개인 경우, 올바른 순서로 유지할 수 있습니까?

동일한 이벤트를 여러 번 바인딩 해야하는 경우 가장 좋은 방법은 js-file 또는 js-code로드 순서를 제어하는 ​​것입니다. jquery는 이벤트 데이터를 대기열로 처리 할 수 ​​있습니다. 순서는 선입 선출입니다. 코드를 변경할 필요가 없습니다. 로드 순서를 변경하십시오.


0

다음은 jQuery의 다양한 버전을 다루는 내 샷입니다.

// Binds a jQuery event to elements at the start of the event chain for that type.
jQuery.extend({
    _bindEventHandlerAtStart: function ($elements, eventType, handler) {
        var _data;

        $elements.bind(eventType, handler);
        // This bound the event, naturally, at the end of the event chain. We
        // need it at the start.

        if (typeof jQuery._data === 'function') {
            // Since jQuery 1.8.1, it seems, that the events object isn't
            // available through the public API `.data` method.
            // Using `$._data, where it exists, seems to work.
            _data = true;
        }

        $elements.each(function (index, element) {
            var events;

            if (_data) {
                events = jQuery._data(element, 'events')[eventType];
            } else {
                events = jQuery(element).data('events')[eventType];
            }

            events.unshift(events.pop());

            if (_data) {
                jQuery._data(element, 'events')[eventType] = events;
            } else {
                jQuery(element).data('events')[eventType] = events;
            }
        });
    }
});

0

일부 특수한 경우 클릭 이벤트 바인딩 방식을 변경할 수없고 (이벤트 바인딩이 다른 코드에서 작성 됨) HTML 요소를 변경할 수있는 경우 가능한 해결책이 있습니다 ( 경고 : 권장되는 바인딩 방법 은 아닙니다) 이벤트, 다른 개발자가 당신을 살해 할 수 있습니다) :

<span onclick="yourEventHandler(event)">Button</span>

이 바인딩 방법을 사용하면 이벤트 처리기가 먼저 추가되므로 먼저 실행됩니다.


그것은 해결책이 아니라 해결책에 대한 힌트입니다. 이 "onclick"핸들러가 이미 (jQuery) 핸들러가있는 요소에서 진행 중이거나 <span> 이 jQuery 핸들러로 요소를 래핑 하고 있습니까?
Auspex

-2

JQuery 1.5는 약속을 소개하며 실행 순서를 제어하는 ​​가장 간단한 구현입니다. http://api.jquery.com/jquery.when/의 전체 설명서

$.when( $('#myDiv').css('background-color', 'red') )
 .then( alert('hi!') )
 .then( myClickFunction( $('#myID') ) )
 .then( myThingToRunAfterClick() );
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.