이 문서의 솔루션은 Javascript 기본객체 그리고 호스트 객체(dom 요소)는
또는
var input = document.getElementsByTagName('input')[0]; var form = document.getElementsByTagName('form')[0]; Evt.on(input, 'click', function(evt){ console.log('input click1'); console.log(evt.target === input); console.log(evt.modified); //evt.stopPropagation(); console.log(evt.modified); }); var handle2 = Evt.on(input, 'click', function(evt){ console.log('input click2'); console.log(evt.target === input); console.log(evt.modified); }); Evt.on(form, 'click', function(evt){ console.log('form click'); console.log(evt.currentTarget === input); console.log(evt.target === input); console.log(evt.currentTarget === form); console.log(evt.modified); }); Evt.emit(input, 'click'); Evt.emit(input, 'click', {bubbles: true}); handle2.remove(); Evt.emit(input, 'click');방식으로 이벤트
를 바인딩하고 트리거합니다.
After네이티브 객체에 이벤트를 추가하는 과정은 주로 after 함수에서 완료됩니다.댓글을 참고해주세요 독자님들도 따라해서 다시 실행해보시길 바랍니다
var after = function(target, method, cb, originalArgs){ var existing = target[method]; var dispatcher = existing; if (!existing || existing.target !== target) { //如果target中没有method方法,则为他添加一个方法method方法 //如果target已经拥有method方法,但target[method]中target不符合要求则将method方法他替换 dispatcher = target[method] = function(){ //由于js是此法作用域:通过阅读包括变量定义在内的数行源码就能知道变量的作用域。 //局部变量在声明它的函数体内以及其所嵌套的函数内始终是有定义的 //所以在这个函数中可以访问到dispatcher变量 var results = null; var args = arguments; if (dispatcher.around) {//如果原先拥有method方法,先调用原始method方法 //此时this关键字指向target所以不用target results = dispatcher.around.advice.apply(this, args); } if (dispatcher.after) {//如果存在after链则依次访问其中的advice方法 var _after = dispatcher.after; while(_after && _after.advice) { //如果需要原始参数则传入arguments否则使用上次执行结果作为参数 args = _after.originalArgs ? arguments : results; results = _after.advice.apply(this, args); _after = _after.next; } } } if (existing) { //函数也是对象,也可以拥有属性跟方法 //这里将原有的method方法放到dispatcher中 dispatcher.around = { advice: function(){ return existing.apply(target, arguments); } } } dispatcher.target = target; } var signal = { originalArgs: originalArgs,//对于每个cb的参数是否使用最初的arguments advice: cb, remove: function() { if (!signal.advice) { return; } //remove的本质是将cb从函数链中移除,删除所有指向他的链接 var previous = signal.previous; var next = signal.next; if (!previous && !next) { dispatcher.after = signal.advice = null; dispatcher.target = null; delete dispatcher.after; } else if (!next){ signal.advice = null; previous.next = null; signal.previous = null; } else if (!previous){ signal.advice = null; dispatcher.after = next; next.previous = null; signal.next = null; } else { signal.advice = null; previous.next = next; next.previous = previous; signal.previous = null; signal.next = null; } } } var previous = dispatcher.after; if (previous) {//将signal加入到链式结构中,处理指针关系 while(previous && previous.next && (previous = previous.next)){}; previous.next = signal; signal.previous = previous; } else {//如果是第一次使用调用after方法,则dispatcher的after属性指向signal dispatcher.after = signal; } cb = null;//防止内存泄露 return signal; }호환성을 해결하려면
IE 브라우저DOM2 IE9이벤트 처리프로그램부터 지원되었지만 이전 버전의 IE 브라우저에서는 DOM 요소에 이벤트를 추가하는 데 여전히 attachmentEvent 메서드가 사용됩니다. 다행히 Microsoft는 2016년에 더 이상 IE8을 유지하지 않을 것이라고 발표했습니다. 이는 대부분의 프런트 엔드 개발자에게 의심할 여지 없이 좋은 소식입니다. DOM2 수준 이벤트 핸들러를 지원하지 않는 브라우저에 대한 호환성은 여전히 처리되어야 하며 일반적으로 다음 사항이 필요합니다. be handler:
//1、统一事件触发顺序 function fixAttach(target, type, listener) { debugger; var listener = fixListener(listener); var method = 'on' + type; return after(target, method, listener, true); };이벤트 처리 함수의 this 키워드에 대해, 다음과 같은 클로저(소스)를 통해 해결할 수 있습니다.
이 기사에서는 이 문제도 이런 방식으로 해결합니다.
//1、统一事件触发顺序 function fixAttach(target, type, listener) { debugger; var listener = fixListener(listener); var method = 'on' + type; return after(target, method, listener, true); }; function fixListener(listener) { return function(evt){ //每次调用listenser之前都会调用fixEvent debugger; var e = _fixEvent(evt, this);//this作为currentTarget if (e && e.cancelBubble && (e.currentTarget !== e.target)){ return; } var results = listener.call(this, e); if (e && e.modified) { // 在整个函数链执行完成后将lastEvent回归到原始状态, //利用异步队列,在主程序执行完后再执行事件队列中的程序代码 //常规的做法是在emit中判断lastEvent并设为null //这充分体现了js异步编程的优势,把变量赋值跟清除代码放在一起,避免逻辑分散,缺点是不符合程序员正常思维方式 if(!lastEvent){ setTimeout(function(){ lastEvent = null; }); } lastEvent = e; } return results; } }이벤트 객체의 표준화를 위해 , ie에서 제공하는 기존 속성을 표준 이벤트 속성으로 변환해야 합니다.
_preventDefault, _stopPropagation 및 _stopImmediatePropagation의 세 가지 함수에서 호출되면 리스너는 실행 후 이벤트 개체를 저장하기 위해 변수를 사용합니다(fixListener 참조). ), 후속 이벤트 핸들러가 이벤트 객체 속성에 따라 다음 단계를 수행할 수 있도록 합니다. stopImmediatePropagation 함수는 이 함수의 시뮬레이션을 위해 클로저를 통해 해결됩니다.
위의 fixListener도 마찬가지입니다.
우리는 다른 목적을 위해 이벤트를 표준화한다는 점에 유의해야 합니다. 다음과 같이 Emit 메소드에 매개변수를 설정하여 이벤트 프로세스를 제어할 수 있습니다. Evt.emit(input, 'click');//거품 없음
Evt.emit(input, 'click', {bubbles: true});//거품
내 의견에 따르면 테스트에서는 이벤트를 트리거하기 위해 fireEvent를 사용합니다. 버블링을 방지하기 위해 {bubbles:false}를 설정할 수 없으므로 여기서는 버블링 프로세스를 시뮬레이션하기 위해 Javascript를 사용합니다. 동시에 이 과정에서 이벤트 객체의 고유성도 보장되어야 합니다.
function _fixEvent(evt, sender){ if (!evt) { evt = window.event; } if (!evt) { // emit没有传递事件参数,或者通过input.onclick方式调用 return evt; } if(lastEvent && lastEvent.type && evt.type == lastEvent.type){ //使用一个全局对象来保证在冒泡过程中访问的是同一个event对象 //chrome中整个事件处理过程event是唯一的 evt = lastEvent; } var fixEvent = evt; // bubbles 和cancelable根据每次emit时手动传入参数设置 fixEvent.bubbles = typeof evt.bubbles !== 'undefined' ? evt.bubbles : false; fixEvent.cancelable = typeof evt.cancelable !== 'undefined' ? evt.cancelable : true; fixEvent.currentTarget = sender; if (!fixEvent.target){ // 多次绑定统一事件,只fix一次 fixEvent.target = fixEvent.srcElement || sender; fixEvent.eventPhase = fixEvent.target === sender ? 2 : 3; if (!fixEvent.preventDefault) { fixEvent.preventDefault = _preventDefault; fixEvent.stopPropagation = _stopPropagation; fixEvent.stopImmediatePropagation = _stopImmediatePropagation; } //参考:http://www.php.cn/ if( fixEvent.pageX == null && fixEvent.clientX != null ) { var doc = document.documentElement, body = document.body; fixEvent.pageX = fixEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); fixEvent.pageY = fixEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); } if (!fixEvent.relatedTarget && fixEvent.fromEvent) { fixEvent.relatedTarget = fixEvent.fromEvent === fixEvent.target ? fixEvent.toElement : fixEvent.fromElement; } // 参考: http://www.php.cn/ if (!fixEvent.which && fixEvent.keyCode) { fixEvent.which = fixEvent.keyCode; } } return fixEvent; } function _preventDefault(){ this.defaultPrevented = true; this.returnValue = false; this.modified = true; } function _stopPropagation(){ this.cancelBubble = true; this.modified = true; } function _stopImmediatePropagation(){ this.isStopImmediatePropagation = true; this.modified = true; }
전체 코드가 첨부되어 있습니다:
// 模拟冒泡事件 var sythenticBubble = function(target, type, evt){ var method = 'on' + type; var args = Array.prototype.slice.call(arguments, 2); // 保证使用emit触发dom事件时,event的有效性 if ('parentNode' in target) { var newEvent = args[0] = {}; for (var p in evt) { newEvent[p] = evt[p]; } newEvent.preventDefault = _preventDefault; newEvent.stopPropagation = _stopPropagation; newEvent.stopImmediatePropagation = _stopImmediatePropagation; newEvent.target = target; newEvent.type = type; } do{ if (target && target[method]) { target[method].apply(target, args); } }while(target && (target = target.parentNode) && target[method] && newEvent && newEvent.bubbles); } var emit = function(target, type, evt){ if (target.dispatchEvent && document.createEvent){ var newEvent = document.createEvent('HTMLEvents'); newEvent.initEvent(type, evt && !!evt.bubbles, evt && !!evt.cancelable); if (evt) { for (var p in evt){ if (!(p in newEvent)){ newEvent[p] = evt[p]; } } } target.dispatchEvent(newEvent); } /*else if (target.fireEvent) { target.fireEvent('on' + type);// 使用fireEvent在evt参数中设置bubbles:false无效,所以弃用 } */else { return sythenticBubble.apply(on, arguments); } }
뇌 지도:
BSD 라이선스에 따른 KityMinder Powered by f-cube, Source Bug |
위 내용은 자바스크립트 이벤트 메커니즘 호환 솔루션을 자세히 설명하는 코드와 그림의 내용입니다. 더 많은 관련 내용은 PHP 중국어 홈페이지(www.php.cn)를 참고해주세요!