동적으로 연결된 이벤트 리스너가 있는지 여부를 확인하는 방법은 무엇입니까?


105

내 문제는 다음과 같습니다. 어떻게 든 동적으로 연결된 이벤트 리스너의 존재를 확인할 수 있습니까? 또는 DOM에서 "onclick"(?) 속성의 상태를 어떻게 확인할 수 있습니까? 해결책을 찾기 위해 Stack Overflow와 마찬가지로 인터넷을 검색했지만 운이 없습니다. 내 HTML은 다음과 같습니다.

<a id="link1" onclick="linkclick(event)"> link 1 </a>
<a id="link2"> link 2 </a> <!-- without inline onclick handler -->

그런 다음 Javascript에서 동적으로 생성 된 이벤트 리스너를 두 번째 링크에 연결합니다.

document.getElementById('link2').addEventListener('click', linkclick, false);

코드는 잘 실행되지만 연결된 리스너를 감지하려는 모든 시도는 실패합니다.

// test for #link2 - dynamically created eventlistener
alert(elem.onclick); // null
alert(elem.hasAttribute('onclick')); // false
alert(elem.click); // function click(){[native code]} // btw, what's this?

jsFiddle이 여기 있습니다 . "Add onclick for 2"를 클릭 한 다음 "[link 2]"를 클릭하면 이벤트가 제대로 실행되지만 "Test link 2"는 항상 false를보고합니다. 누군가 도울 수 있습니까?


1
내가 말할 죄송하지만, 현재의 방법을 사용하여 이벤트 바인딩을 취득하는 것은 불가능 : stackoverflow.com/questions/5296858/...
이반

1
크롬 개발 도구를 사용하여 할 수 있습니다 : stackoverflow.com/a/41137585/863115
arufian

청취자를 추적하는 자체 스토리지를 생성하여이를 수행 할 수 있습니다. 자세한 내용은 내 대답 을 참조하십시오 .
Angel Politis 2011

이미 추가 된 이벤트가 다시 추가되는 것을 방지하는 것이 목적이라면 여기에 정답이 있습니다 . 기본적으로 익명 함수 대신 명명 된 함수를 사용하면 중복 된 동일한 이벤트 리스너가 삭제되므로 걱정할 필요가 없습니다.
Syknapse

중복 리스너가 염려되는 경우 다시 추가하기 전에 현재 이벤트 리스너를 제거하십시오. 완벽하지는 않지만 간단합니다.
Jacksonkr

답변:


49

동적으로 연결된 이벤트 리스너가 있는지 여부를 확인할 수있는 방법이 없습니다.

이벤트 리스너가 연결되었는지 확인할 수있는 유일한 방법은 다음과 같이 이벤트 리스너를 연결하는 것입니다.

elem.onclick = function () { console.log (1) }

그런 다음 onclick반환 !!elem.onclick(또는 유사한 항목) 을 통해 이벤트 리스너가 연결되었는지 테스트 할 수 있습니다 .


36

나는 다음과 같이했다.

const element = document.getElementById('div');

if (element.getAttribute('listener') !== 'true') {
     element.addEventListener('click', function (e) {
         const elementClicked = e.target;
         elementClicked.setAttribute('listener', 'true');
         console.log('event has been attached');
    });
}

리스너가 첨부 될 때 요소에 대한 특수 속성을 생성 한 다음 존재하는지 확인합니다.


5
이것이 내가 과거에 사용한 접근 방식입니다. 내 권장 사항은 매우 구체적인 구문 구조를 사용하는 것입니다. IE는 "listener"대신 이벤트 자체를 사용합니다. 그래서 "데이터 이벤트 클릭". 이것은 하나 이상의 이벤트를 수행하려는 이벤트에 약간의 유연성을 제공하고 일을 좀 더 읽기 쉽게 유지합니다.
conrad10781

@ conrad10781의 추가는 가장 신뢰할 수 있고 가장 간단한 방법처럼 보입니다. 어떤 이유로 요소가 다시 렌더링되고 이벤트 리스너의 연결이 끊어지면이 속성도 재설정됩니다.
Arye Eidelman 19-06-25

25

내가 할 일은 FALSE로 시작하고 이벤트를 연결할 때 TRUE로 설정되는 함수 외부에 부울을 만드는 것입니다. 이것은 이벤트를 다시 첨부하기 전에 일종의 플래그 역할을합니다. 여기에 아이디어의 예가 있습니다.

// initial load
var attached = false;

// this will only execute code once
doSomething = function()
{
 if (!attached)
 {
  attached = true;
  //code
 }
} 

//attach your function with change event
window.onload = function()
{
 var txtbox = document.getElementById('textboxID');

 if (window.addEventListener)
 {
  txtbox.addEventListener('change', doSomething, false);
 }
 else if(window.attachEvent)
 {
  txtbox.attachEvent('onchange', doSomething);
 }
}

이렇게하려면 첨부 된 이벤트의 코드를 변경하여 첨부되었는지 여부를 추적해야합니다 ( this 또는 this answer 과 유사 함 ). 코드를 제어하지 않으면 작동하지 않습니다.
user202729


6

tl; dr : 아니요, 기본적으로 지원되는 방식으로는이 작업을 수행 할 수 없습니다.


이를 달성하기 위해 내가 아는 유일한 방법은 추가 된 리스너의 기록을 보관하는 사용자 지정 스토리지 객체를 만드는 것입니다. 다음과 같은 내용 :

/* Create a storage object. */
var CustomEventStorage = [];

1 단계 : 먼저 저장소 개체를 탐색하고 요소가 지정된 요소 (또는 false)의 레코드를 반환 할 수있는 함수가 필요합니다.

/* The function that finds a record in the storage by a given element. */
function findRecordByElement (element) {
    /* Iterate over every entry in the storage object. */
    for (var index = 0, length = CustomEventStorage.length; index < length; index++) {
        /* Cache the record. */
        var record = CustomEventStorage[index];

        /* Check whether the given element exists. */
        if (element == record.element) {
            /* Return the record. */
            return record;
        }
    }

    /* Return false by default. */
    return false;
}

2 단계 : 그런 다음 이벤트 리스너를 추가하고 리스너를 스토리지 객체에 삽입 할 수있는 함수가 필요합니다.

/* The function that adds an event listener, while storing it in the storage object. */
function insertListener (element, event, listener, options) {
    /* Use the element given to retrieve the record. */
    var record = findRecordByElement(element);

    /* Check whether any record was found. */
    if (record) {
        /* Normalise the event of the listeners object, in case it doesn't exist. */
        record.listeners[event] = record.listeners[event] || [];
    }
    else {
        /* Create an object to insert into the storage object. */
        record = {
            element: element,
            listeners: {}
        };

        /* Create an array for event in the record. */
        record.listeners[event] = [];

        /* Insert the record in the storage. */
        CustomEventStorage.push(record);
    }

    /* Insert the listener to the event array. */
    record.listeners[event].push(listener);

    /* Add the event listener to the element. */
    element.addEventListener(event, listener, options);
}

3 단계 : 질문의 실제 요구 사항과 관련하여 지정된 이벤트에 대한 이벤트 리스너에 요소가 추가되었는지 확인하려면 다음 함수가 필요합니다.

/* The function that checks whether an event listener is set for a given event. */
function listenerExists (element, event, listener) {
    /* Use the element given to retrieve the record. */
    var record = findRecordByElement(element);

    /* Check whether a record was found & if an event array exists for the given event. */
    if (record && event in record.listeners) {
        /* Return whether the given listener exists. */
        return !!~record.listeners[event].indexOf(listener);
    }

    /* Return false by default. */
    return false;
}

4 단계 : 마지막으로 스토리지 객체에서 리스너를 삭제할 수있는 함수가 필요합니다.

/* The function that removes a listener from a given element & its storage record. */
function removeListener (element, event, listener, options) {
    /* Use the element given to retrieve the record. */
    var record = findRecordByElement(element);

    /* Check whether any record was found and, if found, whether the event exists. */
    if (record && event in record.listeners) {
        /* Cache the index of the listener inside the event array. */
        var index = record.listeners[event].indexOf(listener);

        /* Check whether listener is not -1. */
        if (~index) {
            /* Delete the listener from the event array. */
            record.listeners[event].splice(index, 1);
        }

        /* Check whether the event array is empty or not. */
        if (!record.listeners[event].length) {
            /* Delete the event array. */
            delete record.listeners[event];
        }
    }

    /* Add the event listener to the element. */
    element.removeEventListener(event, listener, options);
}

단편:


OP가 질문을 게시 한 지 5 년 이상이 지났지 만 앞으로 우연히 발견 한 사람들이이 답변을 통해 도움이 될 것이라고 생각하므로 자유롭게 제안하거나 개선하십시오. 😊


이 솔루션에 감사드립니다. 간단하고 견고합니다. 하지만 한 가지 작은 실수가 있습니다. "removeListener"함수는 이벤트 배열이 비어 있는지 여부를 확인하지만 삼항이 올바르지 않은지 확인합니다. "if (record.listeners [event] .length! =-1)"라고 표시되어야합니다.
JPA

5

예를 들어 Chrome 검사기를 사용하여 EventListener가 존재하는지 항상 수동으로 확인할 수 있습니다. 요소 탭에는 기존의 "스타일"하위 탭이 있으며 그 옆에는 "이벤트 리스너"라는 하위 탭이 있습니다. 링크 된 요소와 함께 모든 EventListener 목록을 제공합니다.


5

이 방법이 존재하지 않는 것이 이상해 보입니다. 드디어 추가 할 때인가?

원한다면 다음과 같이 할 수 있습니다.

var _addEventListener = EventTarget.prototype.addEventListener;
var _removeEventListener = EventTarget.prototype.removeEventListener;
EventTarget.prototype.events = {};
EventTarget.prototype.addEventListener = function(name, listener, etc) {
  var events = EventTarget.prototype.events;
  if (events[name] == null) {
    events[name] = [];
  }

  if (events[name].indexOf(listener) == -1) {
    events[name].push(listener);
  }

  _addEventListener(name, listener);
};
EventTarget.prototype.removeEventListener = function(name, listener) {
  var events = EventTarget.prototype.events;

  if (events[name] != null && events[name].indexOf(listener) != -1) {
    events[name].splice(events[name].indexOf(listener), 1);
  }

  _removeEventListener(name, listener);
};
EventTarget.prototype.hasEventListener = function(name) {
  var events = EventTarget.prototype.events;
  if (events[name] == null) {
    return false;
  }

  return events[name].length;
};

3

다음은 동적으로 연결된 이벤트 리스너가 있는지 확인하는 데 사용한 스크립트입니다. jQuery를 사용하여 이벤트 핸들러를 요소에 연결 한 다음 해당 이벤트 (이 경우 '클릭'이벤트)를 트리거했습니다. 이렇게하면 이벤트 처리기가 연결된 경우에만 존재하는 이벤트 속성을 검색하고 캡처 할 수 있습니다.

var eventHandlerType;

$('#contentDiv').on('click', clickEventHandler).triggerHandler('click');

function clickEventHandler(e) {
    eventHandlerType = e.type;
}

if (eventHandlerType === 'click') {
    console.log('EventHandler "click" has been applied');
}

2
요소에 기본적으로 이벤트가 첨부 된 요소로 표시하는 속성을 요소에 할당 할 수도 있습니다.
Justin

2

특정 요소에 등록 된 이벤트를 검색하는 크로스 브라우저 기능이없는 것 같습니다.

그러나 개발 도구를 사용하여 일부 브라우저에서 요소에 대한 콜백 함수를 볼 수 있습니다. 이것은 웹 페이지가 어떻게 작동하는지 확인하거나 코드를 디버깅 할 때 유용 할 수 있습니다.

Firefox

먼저 개발자 도구 의 Inspector 탭에서 요소를 확인합니다 . 다음과 같이 할 수 있습니다.

  • 페이지 에서 검사하려는 웹 페이지의 항목을 마우스 오른쪽 버튼으로 클릭하고 메뉴에서 "요소 검사"를 선택합니다.
  • 콘솔 내에서 함수를 사용하여 document.querySelector 와 같은 요소를 선택한 다음 요소 옆의 아이콘을 클릭하여 Inspector 탭 에서 볼 수 있습니다.

이벤트가 요소에 등록 된 경우 요소 옆에 이벤트 라는 단어가 포함 된 버튼이 표시됩니다 . 클릭하면 요소에 등록 된 이벤트를 볼 수 있습니다. 이벤트 옆에있는 화살표를 클릭하면 해당 이벤트에 대한 콜백 함수를 볼 수 있습니다.

크롬

먼저 개발자 도구 의 요소 탭에서 요소를 확인 합니다. 다음과 같이 할 수 있습니다.

  • 페이지에 사용자가 메뉴에서 검사하고 선택 "검사"하려는 웹 페이지에있는 항목을 마우스 오른쪽 버튼으로 클릭하여
  • 콘솔 내에서 document.querySelector 와 같은 요소를 선택하는 함수를 사용하고 요소를 마우스 오른쪽 버튼으로 클릭 한 다음 "요소 패널에서 표시"를 선택하여 검사기 탭 에서 확인합니다 .

웹 페이지 요소를 포함하는 트리를 표시하는 창 섹션 근처에 "이벤트 리스너"라는 탭이있는 다른 섹션이 있어야합니다. 요소에 등록 된 이벤트를 보려면 선택하십시오. 특정 이벤트에 대한 코드를 보려면 오른쪽에있는 링크를 클릭하십시오.

Chrome에서 요소에 대한 이벤트는 getEventListeners 함수를 사용하여 찾을 수도 있습니다 . 그러나 내 테스트에 따르면 getEventListeners 함수는 여러 요소가 전달 될 때 이벤트를 나열하지 않습니다. 리스너가있는 페이지에서 모든 요소를 ​​찾고 해당 리스너에 대한 콜백 함수를 보려면 콘솔에서 다음 코드를 사용하여이를 수행 할 수 있습니다.

var elems = document.querySelectorAll('*');

for (var i=0; i <= elems.length; i++) {
    var listeners = getEventListeners(elems[i]);

    if (Object.keys(listeners).length < 1) {
        continue;
    }

    console.log(elems[i]);

    for (var j in listeners) {
        console.log('Event: '+j);

        for (var k=0; k < listeners[j].length; k++) {
            console.log(listeners[j][k].listener);
        }
    }
}

주어진 브라우저 또는 다른 브라우저에서이 작업을 수행하는 방법을 알고있는 경우이 답변을 편집하십시오.


1

내 이벤트가 첨부되었는지 확인하려고 시도하여 이것을 발견했습니다 ....

당신이 할 경우 :

item.onclick

"null"을 반환합니다.

하지만 그렇게한다면 :

item.hasOwnProperty('onclick')

그러면 "TRUE"입니다.

그래서 "addEventListener"를 사용하여 이벤트 핸들러를 추가 할 때 "hasOwnProperty"를 통해서만 액세스 할 수 있다고 생각합니다. 왜, 어떻게 알았 으면 좋겠는데, 조사 끝에 설명을 찾지 못했어요.


2
onclick에서 분리 .addEventListener하면서 속성에 영향을 - .addEventListener하지 않습니다
자크 Saucier

1

내가 잘 이해하면 청취자가 확인되었는지 여부 만 확인할 수 있지만 어떤 청취자가 구체적으로 발표자인지 확인할 수는 없습니다.

따라서 일부 임시 코드는 코딩 흐름을 처리하기 위해 공백을 채울 것입니다. 실용적인 방법은 stateusing 변수 를 만드는 것 입니다. 예를 들어 다음과 같이 리스너의 검사기를 연결합니다.

var listenerPresent=false

그런 다음 리스너를 설정하면 값을 변경하십시오.

listenerPresent=true

그런 다음 eventListener의 콜백 내에서 특정 기능을 내부에 할당 할 수 있으며 같은 방식으로 일부 상태에 따라 기능에 대한 액세스를 변수로 배포 할 수 있습니다.

accessFirstFunctionality=false
accessSecondFunctionality=true
accessThirdFunctionality=true

1

이벤트를 추가하기 전에 제거하십시오.

document.getElementById('link2').removeEventListener('click', linkclick, false);
document.getElementById('link2').addEventListener('click', linkclick, false);

이것은 좋은 속임수이지만 현재 "추가 된"이벤트 리스너 중 하나 인 함수에 대한 참조가있는 경우에만 작동한다고 가정합니다. 이것은 분명히 표준 API의 단점이라고 생각합니다. 이벤트 리스너를 추가 할 수 있다면 지금까지 추가 된 이벤트 리스너도 확인할 수 있습니다. 거의 모호한 개념처럼 보이는 이벤트 리스너가 현재 "쓰기 전용 변수"인 것과 거의 같습니다.
Panu Logic

0

이 작업을 수행 할 수있는 스크립트를 작성했습니다. 그것은 당신에게 두 세계의 기능을 제공합니다 hasEvent(Node elm, String event)그리고 getEvents(Node elm)당신은 활용할 수있다. EventTarget프로토 타입 메서드를 수정하며 add/RemoveEventListenerHTML 마크 업 또는 자바 스크립트 구문을 통해 추가 된 이벤트에 대해서는 작동하지 않습니다.elm.on_event = ...

GitHub에서 더 많은 정보

라이브 데모

스크립트:

var hasEvent,getEvents;!function(){function b(a,b,c){c?a.dataset.events+=","+b:a.dataset.events=a.dataset.events.replace(new RegExp(b),"")}function c(a,c){var d=EventTarget.prototype[a+"EventListener"];return function(a,e,f,g,h){this.dataset.events||(this.dataset.events="");var i=hasEvent(this,a);return c&&i||!c&&!i?(h&&h(),!1):(d.call(this,a,e,f),b(this,a,c),g&&g(),!0)}}hasEvent=function(a,b){var c=a.dataset.events;return c?new RegExp(b).test(c):!1},getEvents=function(a){return a.dataset.events.replace(/(^,+)|(,+$)/g,"").split(",").filter(function(a){return""!==a})},EventTarget.prototype.addEventListener=c("add",!0),EventTarget.prototype.removeEventListener=c("remove",!1)}();

-1

이론적으로 addEventListener 및 removeEventListener를 피기 백하여 'this'개체에 플래그를 제거 할 수 있습니다. 추악하고 나는 테스트하지 않았습니다 ...

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.