4 개의 스크립트 블록을 포함 할 수있는 페이지가있는 웹 응용 프로그램이 있다고 가정 해 보겠습니다. 내가 작성한 스크립트는 해당 블록 중 하나에서 발견 될 수 있지만 컨트롤러가 처리하는 스크립트는 알 수 없습니다.
일부 onclick
이벤트를 버튼에 바인딩 하지만 때로는 예상하지 못한 순서로 실행되는 것을 알았습니다.
주문을 보장 할 수있는 방법이 있습니까, 아니면 과거에이 문제를 어떻게 처리 했습니까?
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);
참고 사항 :
this.data("events")
여기를 참조하십시오 stackoverflow.com/questions/12214654/…
순서가 중요한 경우 다른 콜백에 의해 해당 이벤트가 트리거 될 때 자체 이벤트를 생성하고 콜백을 시작하여 바인딩 할 수 있습니다.
$('#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 객체로 영리한 작업을 수행 할 수도 있습니다.
.delegate
더 이상 사용되지 않으며 이제 사용해야합니다 .on
,하지만 난 당신과 같은 문제가 도달 할 수있는 방법을 모른다on
바운드 콜백이 호출되는 순서는 각 jQuery 객체의 이벤트 데이터에 의해 관리됩니다. 해당 데이터를 직접보고 조작 할 수있는 함수 (내가 아는 것)는 없으며 bind () 및 unbind () (또는 동등한 도우미 함수) 만 사용할 수 있습니다.
Dowski의 방법이 가장 좋습니다. "실제"이벤트에 바인딩 된 "첫 번째"콜백을 사용하여 정렬 된 일련의 사용자 지정 이벤트에 바인딩하도록 다양한 바인딩 된 콜백을 수정해야합니다. 그렇게하면 순서에 관계없이 시퀀스가 올바른 방식으로 실행됩니다.
내가 볼 수있는 유일한 대안은 실제로 생각하고 싶지 않은 것입니다. 함수의 바인딩 구문이 바인딩되기 전에 알고 있다면 해당 함수를 모두 바인딩 해제 한 다음 다시 바인딩하십시오. 적절한 순서대로. 이제 코드가 중복되었으므로 문제가 발생했습니다.
jQuery를 사용하여 객체의 이벤트 데이터에서 바운드 이벤트의 순서를 간단하게 변경할 수는 있지만 가능하지 않은 jQuery 코어에 연결하는 코드를 작성하지 않고도 멋질 것입니다. 그리고 내가 생각하지 않은 것을 허용하면 의미가있을 수 있으므로 의도적으로 누락되었을 수 있습니다.
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();
});
핸들러를 정상적으로 바인드하고 다음을 실행하십시오.
element.data('events').action.reverse();
예를 들어 :
$('#mydiv').data('events').click.reverse();
$._data(element, 'events')[name].reverse()
합니다
다음과 같이 시도해보십시오.
/**
* 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]);
}
}
나는 같은 문제가 있으며이 주제를 찾았습니다. 위의 답변으로 이러한 문제를 해결할 수 있지만 좋은 계획이라고 생각하지 않습니다.
우리는 현실 세계에 대해 생각합시다.
이러한 답변을 사용하려면 코드를 변경해야합니다. 코드 스타일을 변경해야합니다. 이 같은:
실물:
$('form').submit(handle);
마구 자르기:
bindAtTheStart($('form'),'submit',handle);
시간이 지남에 따라 프로젝트에 대해 생각하십시오. 코드는보기 흉하고 읽기 어렵다! 간단한 이유는 항상 더 좋습니다. bindAtTheStart가 10 개이면 버그가 없을 수 있습니다. bindAtTheStart가 100 개인 경우, 올바른 순서로 유지할 수 있습니까?
동일한 이벤트를 여러 번 바인딩 해야하는 경우 가장 좋은 방법은 js-file 또는 js-code로드 순서를 제어하는 것입니다. jquery는 이벤트 데이터를 대기열로 처리 할 수 있습니다. 순서는 선입 선출입니다. 코드를 변경할 필요가 없습니다. 로드 순서를 변경하십시오.
다음은 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;
}
});
}
});
일부 특수한 경우 클릭 이벤트 바인딩 방식을 변경할 수없고 (이벤트 바인딩이 다른 코드에서 작성 됨) HTML 요소를 변경할 수있는 경우 가능한 해결책이 있습니다 ( 경고 : 권장되는 바인딩 방법 은 아닙니다) 이벤트, 다른 개발자가 당신을 살해 할 수 있습니다) :
<span onclick="yourEventHandler(event)">Button</span>
이 바인딩 방법을 사용하면 이벤트 처리기가 먼저 추가되므로 먼저 실행됩니다.
JQuery 1.5는 약속을 소개하며 실행 순서를 제어하는 가장 간단한 구현입니다. http://api.jquery.com/jquery.when/의 전체 설명서
$.when( $('#myDiv').css('background-color', 'red') )
.then( alert('hi!') )
.then( myClickFunction( $('#myID') ) )
.then( myThingToRunAfterClick() );