IT박스

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

itboxs 2020. 5. 30. 22:27
반응형

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


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

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

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


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

그것이 사용 중이라면, 항상 다른 것들보다 먼저 트리거되는 이벤트 리스너를 바인딩하는 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 () 및 기타 관련 함수를 사용하지 않고 다른 방식으로 바인딩 된 이벤트 리스너 전에 반드시 트리거되지는 않습니다.

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

$('#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;
});

적어도 그런 것.


모든 콜백이 항상 존재하고 서로 의존하는 것에 만족한다면 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 객체로 영리한 작업을 수행 할 수도 있습니다.


The order the bound callbacks are called in is managed by each jQuery object's event data. There aren't any functions (that I know of) that allow you to view and manipulate that data directly, you can only use bind() and unbind() (or any of the equivalent helper functions).

Dowski's method is best, you should modify the various bound callbacks to bind to an ordered sequence of custom events, with the "first" callback bound to the "real" event. That way, no matter in what order they are bound, the sequence will execute in the right way.

The only alternative I can see is something you really, really don't want to contemplate: if you know the binding syntax of the functions may have been bound before you, attempt to un-bind all of those functions and then re-bind them in the proper order yourself. That's just asking for trouble, because now you have duplicated code.

It would be cool if jQuery allowed you to simply change the order of the bound events in an object's event data, but without writing some code to hook into the jQuery core that doesn't seem possible. And there are probably implications of allowing this that I haven't thought of, so maybe it's an intentional omission.


Please note that in the jQuery universe this must be implemented differently as of version 1.8. The following release note is from the jQuery blog:

.data(“events”): jQuery stores its event-related data in a data object named (wait for it) events on each element. This is an internal data structure so in 1.8 this will be removed from the user data name space so it won’t conflict with items of the same name. jQuery’s event data can still be accessed via jQuery._data(element, "events")

We do have complete control of the order in which the handlers will execute in the jQuery universe. Ricoo points this out above. Doesn't look like his answer earned him a lot of love, but this technique is very handy. Consider, for example, any time you need to execute your own handler prior to some handler in a library widget, or you need to have the power to cancel the call to the widget's handler conditionally:

$("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();
});

just bind handler normally and then run:

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

so for example:

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

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;
}

You can try something like this:

/**
  * 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]);
    }   
}

I have same issue and found this topic. the above answers can solve those problem, but I don't think them are good plans.

let us think about the real world.

if we use those answers, we have to change our code. you have to change your code style. something like this:

original:

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

hack:

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

as time goes on, think about your project. the code is ugly and hard to read! anthoer reason is simple is always better. if you have 10 bindAtTheStart, it may no bugs. if you have 100 bindAtTheStart, are you really sure you can keep them in right order?

so if you have to bind same events multiple.I think the best way is control js-file or js-code load order. jquery can handle event data as queue. the order is first-in, first-out. you don't need change any code. just change load order.


Here's my shot at this, covering different versions of 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;
            }
        });
    }
});

In some special cases, when you cannot change how the click events are bound (event bindings are made from others' codes), and you can change the HTML element, here is a possible solution (warning: this is not the recommended way to bind events, other developers may murder you for this):

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

With this way of binding, your event hander will be added first, so it will be executed first.


JQuery 1.5 introduces promises, and here's the simplest implementation I've seen to control order of execution. Full documentation at http://api.jquery.com/jquery.when/

$.when( $('#myDiv').css('background-color', 'red') )
 .then( alert('hi!') )
 .then( myClickFunction( $('#myID') ) )
 .then( myThingToRunAfterClick() );

참고URL : https://stackoverflow.com/questions/290254/how-to-order-events-bound-with-jquery

반응형