Javascript removeEventListener가 작동하지 않습니다.


80

eventListener를 추가하려면 다음 코드가 있습니다.

 area.addEventListener('click',function(event) {
              app.addSpot(event.clientX,event.clientY);
              app.addFlag = 1;
          },true);

예상대로 올바르게 작동합니다. 나중에 다른 함수에서 다음 코드를 사용하여 이벤트 리스너를 제거하려고했습니다.

 area.removeEventListener('click',function(event) {
              app.addSpot(event.clientX,event.clientY);
              app.addFlag = 1;
          },true);

하지만 짝수 리스너가 제거되지 않았습니다. 왜 발생합니까? 내 removeEventListener ()에 문제가 있습니까? 참고 : 여기에 document.getElementById ( 'myId')와 같은 영역이 있습니다.


답변:


129

두 개의 익명 함수가 완전히 다른 함수이기 때문입니다. 귀하 removeEventListener의 인수는 이전에 부착 된 함수 객체에 대한 참조하지 않습니다.

function foo(event) {
              app.addSpot(event.clientX,event.clientY);
              app.addFlag = 1;
          }
 area.addEventListener('click',foo,true);
 area.removeEventListener('click',foo,true);

1
정말 고맙습니다!
Woppi

55
+1 참. bind(this)서명이 변경됩니다. 그래서 항상에 기능을 할당 var바인딩 후 this기능 사용하기 bind때문에 동일한 API var에 사용할 수있다 removeListener. 타이프 스크립트에서이 문제를 더 분명하게 볼 수 있습니다
Nirus 2017 년

4
그러면 함수 매개 변수를 전달할 수 없습니다. fefoo(1)
Herrgott

2
감사는 이전에 부착 된 함수 객체에 대한 참조하지 않습니다
Evhz

18
누군가 수업을 사용하는 경우 this.onClick = this.onClick.bind(this)어떤 청취자보다 먼저 시도해보십시오 btn.addEventListener('click', this.onClick). 마지막으로btn.removeEventListener('click', this.onClick)
joseluisq

13

Windows 개체의 경우 마지막 매개 변수 "true"가 필요합니다. 캡처 플래그가 없으면 제거가 작동하지 않습니다.


내 문제를 해결했습니다. 감사.
James Manes

7

두 호출 모두에서 두 가지 다른 함수를 만들고 있습니다. 따라서 두 번째 기능은 첫 번째 기능과 어떤 식 으로든 관련이 없으며 엔진은 기능을 제거 할 수 있습니다. 대신 함수에 대한 공통 식별자를 사용하십시오.

var handler = function(event) {
              app.addSpot(event.clientX,event.clientY);
              app.addFlag = 1;
          };
area.addEventListener('click', handler,true);

나중에 다음을 호출하여 핸들러를 제거 할 수 있습니다.

area.removeEventListener('click', handler,true);

1

제거하려면 함수를 변수에 저장하거나 이름이 지정된 함수를 사용하고 해당 함수를 removeEventListener호출에 전달 하십시오.

function areaClicked(event) {
    app.addSpot(event.clientX, event.clientY);
    app.addFlag = 1;
}

area.addEventListener('click', areaClicked, true);
// ...
area.removeEventListener('click', areaClicked, true);

1
하지만 어떻게 그 함수에 인수 (여기 이벤트)를 전달할 수
있습니까

브라우저에 의해 전달됩니다. 함수를 개별적으로 정의하는지 여부는 중요하지 않습니다.
ThiefMaster

경고 : 내 접근 방식에 무엇이 잘못되었는지 알아 냈습니다. removeEventListener () 메서드는 NAMED FUNCTIONS에서만 작동합니다. 익명 기능에서는 작동하지 않습니다! 이것을 고려하여 코드를 편집했을 때 모든 것이 계획대로 작동했습니다. 클로저에 NAMED 함수를 정의하고 클로저에 의해 전달 된 매개 변수와 함께 그 인스턴스에 대한 참조를 반환해야합니다. 이렇게하면 removeEventListener ()가 완벽하게 작동합니다.
David Edwards

1

이벤트 리스너가 호출하는 함수에 지역 변수를 전달하려면 함수 내부에 함수를 정의하고 (지역 변수를 가져 오기 위해) 함수 자체에 함수 이름을 전달할 수 있습니다. 예를 들어 app을 로컬 변수로 사용하여 이벤트 리스너를 추가하는 함수 내에서 시작해 보겠습니다. 이 함수 안에 다음과 같은 함수를 작성합니다.

function yourFunction () {
    var app;

    function waitListen () {
        waitExecute(app, waitListen);
    }

    area.addEventListener('click', waitListen, true);
}

그런 다음 waitExecute가 호출 될 때 제거해야하는 것이 있습니다.

function waitExecute (app, waitListen) {
    ... // other code
    area.removeEventListener('click', waitListen, true);
}

여기서 문제가 발생했습니다. 이벤트 핸들러 함수를 정의하고 해당 함수에 대한 참조를 저장 한 다음 나중에 해당 참조를 removeEventListener ()에 전달하더라도 함수는 제거되지 않습니다. 코멘트는 코드를 원한다면, 내가 대답 상자 ... 최대 사용해야합니다, 코드를 작성하려면 너무 작다
데이비드 에드워즈

위의 부록 : 제가 발견 한 또 다른 흥미로운 현상은 이벤트 리스너가 수동적이라고 지정하더라도 이전 이벤트 리스너가 여전히 체인에 남아 있다는 것입니다. 더 나쁜 점은 이전 버전은 이제 차단 이벤트 처리기가되고 새 항목은 수동 상태를 유지한다는 것입니다. 여기에 설명이 필요하다고 생각합니다.
David Edwards

0

먼저 이벤트 핸들러를 정의하고

그리고

area.addEventListener('click',handler);
area.removeEventListener('click',handler);

0

설명이 필요한 removeEventListener () 문제가 발생했습니다.

이벤트 리스너에 매개 변수를 전달할 수 있기를 원했기 때문에 이벤트 리스너를 생성하는 함수를 작성했습니다. 그러면 의도 한 이벤트 리스너를 콜백으로 호출하는 두 번째 함수가 반환됩니다.

전체 라이브러리 파일은 다음과 같습니다.

//Event handler constants

function EventHandlerConstants()
{
this.SUCCESS = 0;   //Signals success of an event handler function
this.NOTFUNCTION = 1;   //actualHandler argument passed to MakeEventHandler() is not a Function object

//End constructor
}

//MakeEventHandler()

//Arguments:

//actualHandler : reference to the actual function to be called as the true event handler

//selfObject    : reference to whatever object is intended to be referenced via the "this" keyword within
//          the true event handler. Set to NULL if no such object is needed by your true
//          event handler specified in the actualHandler argument above.

//args      : array containing the arguments to be passed to the true event handler, so that the true
//          event handler can be written with named arguments, such as:

//          myEventHandler(event, arg1, arg2, ... )

//          If your function doesn't need any arguments, pass an empty array, namely [], as the
//          value of this argument.

//Usage:

//c = new EventHandlerConstants();
//res = MakeEventHandler(actualHandler, selfObject, args);
//if (res == c.SUCCESS)
//  element.addEventListener(eventType, res.actualHandler, true);   //or whatever


function MakeEventHandler(actualHandler, selfObject, args)
{
var c = new EventHandlerConstants();

var funcReturn = null;      //This will contain a reference to the actual function generated and passed back to
                //the caller

var res = {
        "status" : c.SUCCESS,
        "actualHandler" : null
        };

if (IsGenuineObject(actualHandler, Function))
{
    res.actualHandler = function(event) {

        var trueArgs = [event].concat(args);

        actualHandler.apply(selfObject, trueArgs);

    };

}
else
{
    res.status = c.NOTFUNCTION;

//End if/else
}

//Return our result object with appropriate properties set ...

return(res);

//End function
}

그런 다음 이것이 의도 한대로 작동하는지 알아보기 위해 빠른 테스트 페이지를 작성하고 원하는대로 이벤트 처리기를 추가하고 제거 할 수 있도록했습니다.

HTML 테스트 페이지는 다음과 같습니다.

<!DOCTYPE html>
<html>
<head>

<!-- CSS goes here -->

<link rel="stylesheet" type="text/css" href="NewEventTest.css">

<!-- Required JavaScript library files -->

<script language = "JavaScript" src="BasicSupport.js"></script>
<script language = "JavaScript" src="EventHandler6.js"></script>

</head>

<body class="StdC" id="MainApplication">

<button type="button" class="StdC NoSwipe" id="Button1">Try Me Out</button>

<button type="button" class="StdC NoSwipe" id="Button2">Alter The 1st Button</button>

</body>

<script language = "JavaScript" src="NewEventTest.js"></script>

</html>

완전성을 위해 다음과 같은 간단한 CSS 파일도 사용합니다.

/* NewEventTest.css */


/* Define standard display settings classes for a range of HTML elements */

.StdC {

color: rgba(255, 255, 255, 1);
background-color: rgba(0, 128, 0, 1);
font-family: "Book Antiqua", "Times New Roman", "Times", serif;
font-size: 100%;
font-weight: normal;
text-align: center;

}


.NoSwipe {

user-select: none;  /* Stops text from being selectable! */

}

테스트 코드는 다음과 같습니다.

//NewEventTest.js


function GlobalVariables()
{
this.TmpRef1 = null;
this.TmpRef2 = null;
this.TmpRef3 = null;

this.Const1 = null;

this.Handler1 = null;
this.Handler2 = null;
this.Handler3 = null;

this.EventOptions = {"passive" : true, "capture" : true };

//End constructor
}


//Button 1 Initial function

function Button1Initial(event)
{
console.log("Button 1 initial event handler triggered");

//End event handler
}


function Button1Final(event)
{
console.log("Button 1 final event handler triggered");

//End event handler
}


function Button2Handler(event, oldFunc, newFunc)
{
var funcRef = null;

this.removeEventListener("click", oldFunc);
this.addEventListener("click", newFunc, GLOBALS.EventOptions);

//End event handler
}


//Application Setup

GLOBALS = new GlobalVariables();

GLOBALS.Const1 = new EventHandlerConstants();

GLOBALS.TmpRef1 = document.getElementById("Button1");
GLOBALS.TmpRef2 = MakeEventHandler(Button1Initial, null, []);
if (GLOBALS.TmpRef2.status == GLOBALS.Const1.SUCCESS)
{
    GLOBALS.Handler1 = GLOBALS.TmpRef2.actualHandler;
    GLOBALS.TmpRef1.addEventListener("click", GLOBALS.Handler1, GLOBALS.EventOptions);

//End if
}

GLOBALS.TmpRef1 = MakeEventHandler(Button1Final, null, []);
if (GLOBALS.TmpRef1.status == GLOBALS.Const1.SUCCESS)
{
    GLOBALS.Handler3 = GLOBALS.TmpRef1.actualHandler;

//End if
}


GLOBALS.TmpRef1 = document.getElementById("Button2");
GLOBALS.TmpRef2 = document.getElementById("Button1");
GLOBALS.TmpRef3 = Button1Final;
GLOBALS.TmpRef4 = MakeEventHandler(Button2Handler, GLOBALS.TmpRef2, [GLOBALS.Handler1, GLOBALS.Handler3]);
if (GLOBALS.TmpRef4.status == GLOBALS.Const1.SUCCESS)
{
    GLOBALS.Handler2 = GLOBALS.TmpRef4.actualHandler;
    GLOBALS.TmpRef1.addEventListener("click", GLOBALS.Handler2, GLOBALS.EventOptions);

//End if
}

따라서 수행 할 테스트는 다음과 같습니다.

[1] 클릭 이벤트 핸들러를 버튼 # 1에 연결합니다.

[2] 버튼을 클릭 할 때 이벤트 핸들러가 호출되는지 테스트합니다.

[3] 테스트가 통과되면 Button # 2를 클릭하고 그에 연결된 이벤트 핸들러를 호출합니다. 그러면 Button # 1에 연결된 이전 이벤트 핸들러가 제거되고 새 이벤트 핸들러로 대체됩니다.

단계 [1] 및 [2]는 정상적으로 작동합니다. 이벤트 핸들러가 첨부되고 버튼을 클릭 할 때마다 호출됩니다.

문제는 단계 [3]에 있습니다.

특히 단계 [3]에서 해당 이벤트 리스너를 제거하기 위해 MakeEventHandler ()에 의해 생성 된 함수에 대한 참조를 저장하더라도 removeEventListener ()를 호출해도 이벤트 리스너가 제거되지 않습니다. 이후에 Button # 1을 클릭하면 내가 제거한 것으로 추정되는 것을 포함하여 두 이벤트 리스너가 모두 실행됩니다!

말할 필요도없이이 동작은 모든 것을 신중하게 설정 했음에도 불구하고 내가 removeEventListener () 호출에서 지정한 함수가 처음에 addEventListener ()로 추가 한 자체 동일한 함수가되도록 설정 했음에도 불구하고 당황스러워 합니다. 주제에 대한 모든 문서에 따르면 '각 호출에 대해 동일한 기능에 대한 참조를 전달 (이 스레드 포함) 읽었 해야 작동하지만 명확하지 않습니다.

단계 [1]에서 예상대로 콘솔의 테스트 출력은 다음과 같습니다.

버튼 1 초기 이벤트 핸들러가 트리거 됨

코드는 예상대로 [2] 단계에서 실행되며 코드를 단계별로 추적하면 실제로 코드가 예상대로 실행됨을 알 수 있습니다.

그러나 단계 [3] 에서 버튼 # 1을 처음 클릭하면 원하는 결과를 얻을 수 있습니다.

버튼 1 최종 이벤트 핸들러가 트리거 됨

무슨 일이 버튼 # 1에 클릭 할 때 발생 이후 이 있습니다 :

버튼 1 초기 이벤트 핸들러가 트리거 됨 버튼 1 최종 이벤트 핸들러가 트리거 됨

확실히, 처음에 Button # 1에 연결된 함수가 클로저 내에서 생성 되었기 때문에 여전히 메모리에 유지 되더라도 요소에 대한 이벤트 리스너 컬렉션에서 분리되어야합니까? 왜 여전히 연결되어 있습니까?

또는 이벤트 리스너와 함께 클로저를 사용하는 것과 관련된 이상한 버그가 발생 했습니까?


2
새로운 질문을해야합니다. 이 영역은 OP의 질문에 답합니다.
VectorVortec

나는 이것도 만났다. 말도 안돼. 지나가는 더러운 방법에 의지해야했다. 창 개체에서 활성화 / 비활성화해야하는 이벤트를 추적합니다.
Mave
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.