program tip

디버깅 할 때 또는 JavaScript 코드에서 DOM 노드에서 이벤트 리스너를 찾는 방법은 무엇입니까?

radiobox 2020. 9. 29. 07:33
반응형

디버깅 할 때 또는 JavaScript 코드에서 DOM 노드에서 이벤트 리스너를 찾는 방법은 무엇입니까?


일부 이벤트 리스너가 입력 상자와 선택 상자에 연결된 페이지가 있습니다. 어떤 이벤트 리스너가 특정 DOM 노드와 어떤 이벤트를 관찰하고 있는지 알아내는 방법이 있습니까?

이벤트는 다음을 사용하여 첨부됩니다.

  1. 프로토 타입의 Event.observe ;
  2. DOM의 addEventListener;
  3. 요소 속성으로 element.onclick.

페이지에서 무슨 일이 일어나고 있는지 검사해야하는 경우 Visual Event 북마크릿을 사용해 볼 수 있습니다 .

업데이트 : 비주얼 이벤트 2 사용 가능;


이벤트가 연결되는 방법에 따라 다릅니다. 설명을 위해 다음과 같은 클릭 핸들러가 있다고 가정합니다.

var handler = function() { alert('clicked!') };

검사를 허용하는 방법과 그렇지 않은 방법을 사용하여 요소에 연결합니다.

방법 A) 단일 이벤트 핸들러

element.onclick = handler;
// inspect
alert(element.onclick); // alerts "function() { alert('clicked!') }"

방법 B) 다중 이벤트 핸들러

if(element.addEventListener) { // DOM standard
    element.addEventListener('click', handler, false)
} else if(element.attachEvent) { // IE
    element.attachEvent('onclick', handler)
}
// cannot inspect element to find handlers

방법 C) : jQuery

$(element).click(handler);
  • 1.3.x

    // inspect
    var clickEvents = $(element).data("events").click;
    jQuery.each(clickEvents, function(key, value) {
        alert(value) // alerts "function() { alert('clicked!') }"
    })
    
  • 1.4.x (객체 내부에 핸들러 저장)

    // inspect
    var clickEvents = $(element).data("events").click;
    jQuery.each(clickEvents, function(key, handlerObj) {
        alert(handlerObj.handler) // alerts "function() { alert('clicked!') }"
        // also available: handlerObj.type, handlerObj.namespace
    })
    

( jQuery.fn.data참조 jQuery.data)

방법 D) : 프로토 타입 (지저분 함)

$(element).observe('click', handler);
  • 1.5.x

    // inspect
    Event.observers.each(function(item) {
        if(item[0] == element) {
            alert(item[2]) // alerts "function() { alert('clicked!') }"
        }
    })
    
  • 1.6 ~ 1.6.0.3 (포함) (여기서는 매우 어렵습니다)

    // inspect. "_eventId" is for < 1.6.0.3 while 
    // "_prototypeEventID" was introduced in 1.6.0.3
    var clickEvents = Event.cache[element._eventId || (element._prototypeEventID || [])[0]].click;
    clickEvents.each(function(wrapper){
        alert(wrapper.handler) // alerts "function() { alert('clicked!') }"
    })
    
  • 1.6.1 (조금 더 좋음)

    // inspect
    var clickEvents = element.getStorage().get('prototype_event_registry').get('click');
    clickEvents.each(function(wrapper){
        alert(wrapper.handler) // alerts "function() { alert('clicked!') }"
    })
    

getEventListeners(domElement)개발자 도구 콘솔에서 Chrome, Firefox, Vivaldi 및 Safari를 지원 합니다.

대부분의 디버깅 목적으로이 방법을 사용할 수 있습니다.

다음은이를 사용하기위한 아주 좋은 참고 자료입니다. https://developers.google.com/chrome-developer-tools/docs/commandline-api#geteventlistenersobject


이제 Chrome 또는 Safari 브라우저의 WebKit Inspector에서이 작업을 수행합니다. 요소 창에서 선택하면 DOM 요소에 대한 이벤트 리스너가 표시됩니다.


자바 스크립트에서 모든 이벤트 리스너를 나열하는 것이 가능합니다 . 그렇게 어렵지 않습니다. 리스너 추가 하기 전에prototype HTML 요소의 메소드 를 해킹하면 됩니다.

function reportIn(e){
    var a = this.lastListenerInfo[this.lastListenerInfo.length-1];
    console.log(a)
}


HTMLAnchorElement.prototype.realAddEventListener = HTMLAnchorElement.prototype.addEventListener;

HTMLAnchorElement.prototype.addEventListener = function(a,b,c){
    this.realAddEventListener(a,reportIn,c); 
    this.realAddEventListener(a,b,c); 
    if(!this.lastListenerInfo){  this.lastListenerInfo = new Array()};
    this.lastListenerInfo.push({a : a, b : b , c : c});
};

이제 모든 앵커 요소 ( a) lastListenerInfo에는 모든 리스너를 포함 하는 속성 이 있습니다 . 또한 익명 함수로 리스너를 제거하는데도 작동합니다.


Google 크롬 에서 getEventListeners 사용 :

getEventListeners(document.getElementByID('btnlogin'));
getEventListeners($('#btnlogin'));

( 여기와 관련 있으므로이 질문 의 답변을 다시 작성 하십시오.)

디버깅 할 때 이벤트 만보고 싶다면 다음 중 하나를 권장합니다.

  1. 비주얼 이벤트
  2. 요소 크롬의 개발자 도구 섹션 : (파이어 폭스에서 유사) 우측 하단의 "이벤트 리스너"에 대한 요소와 모양을 선택

코드에서 이벤트를 사용하고 싶고 버전 1.8 이전의 jQuery를 사용하는 경우 다음을 사용할 수 있습니다.

$(selector).data("events")

이벤트를 얻으려면. 버전 1.8부터 .data ( "events") 사용이 중단되었습니다 ( 이 버그 티켓 참조 ). 당신이 사용할 수있는:

$._data(element, "events")

또 다른 예 : 콘솔에 대한 특정 링크의 모든 클릭 이벤트를 작성합니다.

var $myLink = $('a.myClass');
console.log($._data($myLink[0], "events").click);

( 작동 예제는 http://jsfiddle.net/HmsQC/ 참조 )

안타깝게도 $ ._ data를 사용 하는 것은 내부 jQuery 구조이고 향후 릴리스에서 변경 될 수 있으므로 디버깅을 제외하고 는 권장되지 않습니다 . 안타깝게도 이벤트에 쉽게 접근 할 수있는 다른 방법은 없습니다.


1 : Prototype.observeElement.addEventListener 사용 ( 소스 코드 참조 )

2 : Element.addEventListener추가 된 리스너를 기억하도록 재정의 수 있습니다 ( EventListenerListDOM3 사양 제안에서 편리한 속성 이 제거됨). 이벤트가 연결되기 전에이 코드를 실행하십시오.

(function() {
  Element.prototype._addEventListener = Element.prototype.addEventListener;
  Element.prototype.addEventListener = function(a,b,c) {
    this._addEventListener(a,b,c);
    if(!this.eventListenerList) this.eventListenerList = {};
    if(!this.eventListenerList[a]) this.eventListenerList[a] = [];
    this.eventListenerList[a].push(b);
  };
})();

모든 이벤트를 읽으십시오 :

var clicks = someElement.eventListenerList.click;
if(clicks) clicks.forEach(function(f) {
  alert("I listen to this function: "+f.toString());
});

그리고 Element.removeEventListenercustom에서 이벤트를 제거 하기 위해 재정의 하는 것을 잊지 마십시오 Element.eventListenerList.

3 : Element.onclick속성은 여기에서 특별한주의가 필요합니다.

if(someElement.onclick)
  alert("I also listen tho this: "+someElement.onclick.toString());

4 : Element.onclick콘텐츠 속성을 잊지 마세요 . 다음 두 가지가 있습니다.

someElement.onclick = someHandler; // IDL attribute
someElement.setAttribute("onclick","otherHandler(event)"); // content attribute

따라서 처리해야합니다.

var click = someElement.getAttribute("onclick");
if(click) alert("I even listen to this: "+click);

Visual Event 북마크릿 (가장 인기있는 답변에서 언급 됨)은 사용자 지정 라이브러리 처리기 캐시 만 훔칩니다.

특정 요소에 어떤 이벤트 리스너가 연결되어 있는지 알아 내기 위해 W3C 권장 DOM 인터페이스에서 제공하는 표준 방법이없는 것으로 나타났습니다. 이것이 감독 인 것처럼 보일 수 있지만, 레벨 3 DOM 사양에 eventListenerList라는 속성을 포함하라는 제안이 있었지만 안타깝게도 이후 초안에서 제거되었습니다. 따라서 우리는 일반적으로 연결된 이벤트의 캐시를 유지하는 개별 Javascript 라이브러리를 살펴 봐야합니다 (나중에 제거하고 다른 유용한 추상화를 수행 할 수 있음).

따라서 Visual Event가 이벤트를 표시하려면 Javascript 라이브러리에서 이벤트 정보를 구문 분석 할 수 있어야합니다.

요소 재정의는 의심 스러울 수 있지만 (즉, JS로 코딩 할 수없는 라이브 컬렉션과 같은 일부 DOM 특정 기능이 있기 때문에) eventListenerList 지원을 기본적으로 제공하고 Chrome, Firefox 및 Opera에서 작동합니다 (IE7에서는 작동하지 않음). ).


이벤트 리스너를 관리하기 위해 기본 DOM 메서드를 래핑 할 수 있습니다 <head>.

<script>
    (function(w){
        var originalAdd = w.addEventListener;
        w.addEventListener = function(){
            // add your own stuff here to debug
            return originalAdd.apply(this, arguments);
        };

        var originalRemove = w.removeEventListener;
        w.removeEventListener = function(){
            // add your own stuff here to debug
            return originalRemove.apply(this, arguments);
        };
    })(window);
</script>

H / T @ les2


이제 Firefox 개발자 도구가이 작업을 수행합니다. 이벤트는 jQueryDOM 이벤트를 포함하여 각 요소의 디스플레이 오른쪽에있는 "ev"버튼을 클릭하여 표시됩니다 .

인스펙터 탭에있는 Firefox 개발자 도구의 이벤트 리스너 버튼 스크린 샷


당신이있는 경우 방화범을 , 당신이 사용할 수있는 console.dir(object or array)자바 스크립트 스칼라, 배열 또는 객체의 콘솔 로그에 좋은 나무를 인쇄 할 수 있습니다.

시험:

console.dir(clickEvents);

또는

console.dir(window);

Jan Turon의 답변을 기반으로 완벽하게 작동하는 솔루션 - getEventListeners()콘솔 에서처럼 작동합니다 .

(중복에 약간의 버그가 있습니다. 어쨌든 많이 깨지지 않습니다.)

(function() {
  Element.prototype._addEventListener = Element.prototype.addEventListener;
  Element.prototype.addEventListener = function(a,b,c) {
    if(c==undefined)
      c=false;
    this._addEventListener(a,b,c);
    if(!this.eventListenerList)
      this.eventListenerList = {};
    if(!this.eventListenerList[a])
      this.eventListenerList[a] = [];
    //this.removeEventListener(a,b,c); // TODO - handle duplicates..
    this.eventListenerList[a].push({listener:b,useCapture:c});
  };

  Element.prototype.getEventListeners = function(a){
    if(!this.eventListenerList)
      this.eventListenerList = {};
    if(a==undefined)
      return this.eventListenerList;
    return this.eventListenerList[a];
  };
  Element.prototype.clearEventListeners = function(a){
    if(!this.eventListenerList)
      this.eventListenerList = {};
    if(a==undefined){
      for(var x in (this.getEventListeners())) this.clearEventListeners(x);
        return;
    }
    var el = this.getEventListeners(a);
    if(el==undefined)
      return;
    for(var i = el.length - 1; i >= 0; --i) {
      var ev = el[i];
      this.removeEventListener(a, ev.listener, ev.useCapture);
    }
  };

  Element.prototype._removeEventListener = Element.prototype.removeEventListener;
  Element.prototype.removeEventListener = function(a,b,c) {
    if(c==undefined)
      c=false;
    this._removeEventListener(a,b,c);
      if(!this.eventListenerList)
        this.eventListenerList = {};
      if(!this.eventListenerList[a])
        this.eventListenerList[a] = [];

      // Find the event in the list
      for(var i=0;i<this.eventListenerList[a].length;i++){
          if(this.eventListenerList[a][i].listener==b, this.eventListenerList[a][i].useCapture==c){ // Hmm..
              this.eventListenerList[a].splice(i, 1);
              break;
          }
      }
    if(this.eventListenerList[a].length==0)
      delete this.eventListenerList[a];
  };
})();

용법:

someElement.getEventListeners([name]) -이름이 설정된 경우 이벤트 리스너 목록을 반환합니다. 해당 이벤트에 대한 리스너 배열을 반환합니다.

someElement.clearEventListeners([name]) -이름이 설정된 경우 모든 이벤트 리스너를 제거합니다. 해당 이벤트에 대한 리스너 만 제거합니다.


Opera 12 (최신 Chrome Webkit 엔진 기반이 아님) Dragonfly 는 한동안 이것을 가지고 있었고 분명히 DOM 구조에 표시됩니다. 내 생각에 그것은 우수한 디버거이며 내가 여전히 Opera 12 기반 버전을 사용하는 유일한 이유입니다 (v13, v14 버전은없고 v15 Webkit 기반에는 여전히 Dragonfly가 없습니다)

여기에 이미지 설명 입력


프로토 타입 1.7.1 방식

function get_element_registry(element) {
    var cache = Event.cache;
    if(element === window) return 0;
    if(typeof element._prototypeUID === 'undefined') {
        element._prototypeUID = Element.Storage.UID++;
    }
    var uid =  element._prototypeUID;           
    if(!cache[uid]) cache[uid] = {element: element};
    return cache[uid];
}

jQuery 2.1에서 그렇게하려고하는데 " $().click() -> $(element).data("events").click;"방법으로는 작동하지 않습니다.

내 경우에는 $ ._ data () 함수 만 작동한다는 것을 깨달았습니다.

	$(document).ready(function(){

		var node = $('body');
		
        // Bind 3 events to body click
		node.click(function(e) { alert('hello');  })
			.click(function(e) { alert('bye');  })
			.click(fun_1);

        // Inspect the events of body
		var events = $._data(node[0], "events").click;
		var ev1 = events[0].handler // -> function(e) { alert('hello')
		var ev2 = events[1].handler // -> function(e) { alert('bye')
		var ev3 = events[2].handler // -> function fun_1()
        
		$('body')
			.append('<p> Event1 = ' + eval(ev1).toString() + '</p>')
			.append('<p> Event2 = ' + eval(ev2).toString() + '</p>')
			.append('<p> Event3 = ' + eval(ev3).toString() + '</p>');        
	
	});

	function fun_1() {
		var txt = 'text del missatge';	 
		alert(txt);
	}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<body>
</body>


멋진 jQuery Events 확장이 있습니다 .

여기에 이미지 설명 입력(주제 출처 )


최근에 이벤트 작업을하면서 페이지의 모든 이벤트를보고 제어하고 싶었습니다. 가능한 솔루션을 살펴본 후, 나만의 방식으로 이벤트를 모니터링하는 맞춤형 시스템을 만들기로 결정했습니다. 그래서 세 가지를했습니다.

먼저 페이지의 모든 이벤트 리스너를위한 컨테이너가 필요했습니다. 이것이 바로 EventListeners객체입니다. 그것은 세 가지 유용한 방법이 있습니다 add(), remove()하고 get().

다음으로, 나는 만든 EventListener이벤트에 대한 필요한 정보를 보유하는 개체를 예 : target, type, callback, options, useCapture, wantsUntrusted, 및 방법 추가 remove()리스너를 제거합니다.

마지막으로, 내가 만든 개체 ( ) 와 함께 작동하도록 네이티브 addEventListener()removeEventListener()메서드를 확장했습니다 .EventListenerEventListeners

용법:

var bodyClickEvent = document.body.addEventListener("click", function () {
    console.log("body click");
});

// bodyClickEvent.remove();

addEventListener()EventListener객체를 생성하고 객체에 추가 EventListeners하고 반환 EventListener하므로 나중에 제거 할 수 있습니다.

EventListeners.get()페이지에서 리스너를 보는 데 사용할 수 있습니다. EventTarget또는 문자열 (이벤트 유형)을 허용합니다 .

// EventListeners.get(document.body);
// EventListeners.get("click");

데모

이 현재 페이지의 모든 이벤트 리스너를 알고 싶다고 가정 해 보겠습니다. 우리는 그렇게 할 수 있습니다 (이 경우 스크립트 관리자 확장 프로그램 인 Tampermonkey를 사용한다고 가정). 다음 스크립트는이를 수행합니다.

// ==UserScript==
// @name         New Userscript
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @include      https://stackoverflow.com/*
// @grant        none
// ==/UserScript==

(function() {
    fetch("https://raw.githubusercontent.com/akinuri/js-lib/master/EventListener.js")
        .then(function (response) {
            return response.text();
        })
        .then(function (text) {
            eval(text);
            window.EventListeners = EventListeners;
        });
})(window);

모든 리스너를 나열하면 299 개의 이벤트 리스너가 있다고 표시됩니다. 일부 중복이있는 것처럼 보이지만 실제로 중복인지 모르겠습니다. 모든 이벤트 유형이 중복되는 것은 아니므로 모든 "중복"이 개별 리스너가 될 수 있습니다.

이 페이지의 모든 이벤트 리스너를 나열하는 콘솔 스크린 샷

저장소 에서 코드를 찾을 수 있습니다 . 꽤 길기 때문에 여기에 게시하고 싶지 않았습니다.


업데이트 : 이것은 jQuery에서 작동하지 않는 것 같습니다. EventListener를 살펴보면 콜백이

function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}

나는 이것이 jQuery에 속하며 실제 콜백이 아니라고 생각합니다. jQuery는 EventTarget의 속성에 실제 콜백을 저장합니다.

$(document.body).click(function () {
    console.log("jquery click");
});

여기에 이미지 설명 입력

이벤트 리스너를 제거하려면 실제 콜백을 removeEventListener()메서드에 전달해야합니다 . 따라서 jQuery로이 작업을 수행하려면 추가 수정이 필요합니다. 나중에 고칠 수 있습니다.


이러한 기능을 변경하면 추가 된 리스너를 기록 할 수 있습니다.

EventTarget.prototype.addEventListener
EventTarget.prototype.attachEvent
EventTarget.prototype.removeEventListener
EventTarget.prototype.detachEvent

나머지 청취자를 읽으십시오.

console.log(someElement.onclick);
console.log(someElement.getAttribute("onclick"));

참고 URL : https://stackoverflow.com/questions/446892/how-to-find-event-listeners-on-a-dom-node-when-debugging-or-from-the-javascript

반응형