removeEventListener trên các hàm ẩn danh trong JavaScript


101

Tôi có một đối tượng có các phương thức trong đó. Các phương thức này được đưa vào đối tượng bên trong một hàm ẩn danh. Nó trông như thế này:

var t = {};
window.document.addEventListener("keydown", function(e) {
    t.scroll = function(x, y) {
        window.scrollBy(x, y);
    };
    t.scrollTo = function(x, y) {
        window.scrollTo(x, y);
    };
});  

(có rất nhiều mã nữa, nhưng điều này đủ để cho thấy vấn đề)

Bây giờ tôi muốn dừng trình nghe sự kiện trong một số trường hợp. Vì vậy, tôi đang cố gắng thực hiện removeEventListener nhưng tôi không thể tìm ra cách thực hiện việc này. Tôi đã đọc trong các câu hỏi khác rằng không thể gọi removeEventListener trên các hàm ẩn danh, nhưng đây cũng là trường hợp của trường hợp này?

Tôi có một phương thức trong t được tạo bên trong hàm ẩn danh và do đó tôi nghĩ rằng nó có thể. Trông như thế này:

t.disable = function() {
    window.document.removeEventListener("keydown", this, false);
}

Tại sao tôi không thể làm điều này?

Có cách nào khác (tốt) để làm điều này không?

Thông tin tiền thưởng; điều này chỉ có thể hoạt động trong Safari, do đó không hỗ trợ IE.


Tại sao không lưu chức năng này? Trình xử lý sự kiện có thể không phải là một chức năng ẩn danh.
kirilloid

2
Tôi nhận ra điều này là muộn một chút, nhưng bạn cũng có thể sử dụng Node.setUserData phương pháp /Node.getUserData để lưu trữ dữ liệu về một phần tử. Ví dụ: khi bạn cần đặt một trình nghe anon (và có thể xóa nó), trước tiên hãy đặt userdata thành một hàm anon (Elem.setUserData('eventListener', function(e){console.log('Event fired.');}, null);và sau đó thực hiện Elem.addEventListener ('event', Elem.getUserData ('eventListener'), false); ... và tương tự đối với removeEventListener. Hy vọng bạn có thể thấy điều này ổn.
Đuổi theo

CHỈNH SỬA: Theo nhận xét trước đó, tôi đoán rằng điều đó chỉ hoạt động trên Firefox ... Tôi vừa thử IE8 (IE9 không xác định), Safari 5.1.2, Chrome (?), Opera 11..Không có xúc xắc
Chase

Câu trả lời:


76

Tôi tin rằng đó là điểm của một chức năng ẩn danh, nó thiếu tên hoặc cách tham chiếu đến nó.

Nếu tôi là bạn, tôi sẽ chỉ tạo một hàm được đặt tên hoặc đặt nó vào một biến để bạn có thể tham chiếu đến nó.

var t = {};
var handler = function(e) {
    t.scroll = function(x, y) {
        window.scrollBy(x, y);
    };
    t.scrollTo = function(x, y) {
        window.scrollTo(x, y);
    };
};
window.document.addEventListener("keydown", handler);

Sau đó, bạn có thể xóa nó bằng cách

window.document.removeEventListener("keydown", handler);   

3
Cảm ơn bạn đã trả lời. Tôi đã đi với: var handler; window.document.addEventListener ("keydown", handler = function (e) {Nhưng điều tôi không hiểu là tại sao "this" không tham chiếu đến trình xử lý sự kiện. Trình xử lý sự kiện có phải là một đối tượng không?
bitkid

1
Các thistừ khóa có thể gây nhầm lẫn. Một nơi tốt để đọc về nó là quirksmode.org/js/this.html
Adam Heath

Cảm ơn rât nhiều. Điều này là hữu ích nhất.
bitkid

Tôi đang cố gắng làm điều này để chặn một quảng cáo thực sự dai dẳng trên một trang web. Tôi biết rằng đây là điểm của các hàm ẩn danh, nhưng điều đó không có nghĩa là tôi không muốn biết cách làm như vậy.
Wyatt8740

@bitkid: Bên trong một hàm xử lý (giả sử nó không phải là một hàm mũi tên), tham chiếu thisđến phần tử mà trình nghe được thêm vào, không phải bản thân sự kiện (sẽ là tham số e). Do đó this === e.currentTarget. đọc developer.mozilla.org/en-US/docs/Web/API/EventTarget/…
chharvey

100

nếu bạn đang ở bên trong hàm thực, bạn có thể sử dụng đối số.callee làm tham chiếu đến hàm. như trong:

button.addEventListener('click', function() {
      ///this will execute only once
      alert('only once!');
      this.removeEventListener('click', arguments.callee);
});

CHỈNH SỬA: Điều này sẽ không hoạt động nếu bạn đang làm việc ở chế độ nghiêm ngặt ( "use strict";)


2
Điều này là tốt vì nó bảo tồn các lợi thế của các chức năng ẩn danh (không làm ô nhiễm không gian tên, v.v.).
bompf

3
cố gắng này trong ứng dụng WinJS, đã nhận lỗi sau: "Truy cập vào 'callee' tài sản của một đối tượng tranh cãi không được cho phép trong chế độ nghiêm ngặt"
Valentin Kantor

1
@ValentinKantor: Đó là bởi vì một cái gì đó trong mã có một "sử dụng nghiêm ngặt"; và bạn không thể sử dụng callee ở chế độ nghiêm ngặt.
OMA

19
Cung cấp cho các chức năng inline một tên và bạn có thể tham khảo nó mà không cần đến arguments.callee:button.addEventListener('click', function handler() { this.removeEventListener('click', handler); });
Harry Tình yêu

4
Như đã nêu trong Mozilla: "Cảnh báo: Phiên bản thứ 5 của ECMAScript (ES5) cấm sử dụng đối số.callee () ở chế độ nghiêm ngặt. Tránh sử dụng đối số.callee () bằng cách đặt tên cho biểu thức hàm hoặc sử dụng khai báo hàm trong đó hàm phải gọi chính nó. "
dude

50

Một phiên bản giải pháp của Otto Nascarella hoạt động ở chế độ nghiêm ngặt là:

button.addEventListener('click', function handler() {
      ///this will execute only once
      alert('only once!');
      this.removeEventListener('click', handler);
});

4
Giải pháp tuyệt vời!
Eric Norcross

2
Đây có thể không phải là cách đúng nhưng đây là điều dễ dàng nhất.
Canh thức

7
window.document.removeEventListener("keydown", getEventListeners(window.document.keydown[0].listener));  

Có thể là một số chức năng ẩn danh, phím xuống 1

Cảnh báo: chỉ hoạt động trong Chrome Dev Tools& không thể sử dụng trong code : link


2
Cảm ơn, bạn đã giải được một câu đố, ít nhất là trong Chrome, vì điều mà nhiều người nói đùa nói là không thể. Trời đất, bạn giống như ... Người dơi!
JasonXA

20
getEventListenersdường như là một phần của Công cụ dành cho nhà phát triển của Chrome, vì vậy không thực sự có thể sử dụng được cho bất kỳ việc gì khác ngoài gỡ lỗi.
DBS

1
Chỉ cần thử nó, xác nhận rằng nó chỉ có sẵn trong Devtools và không có trong script bên trong một trang.
Andres Riofrio

5

trong các trình duyệt hiện đại, bạn có thể làm như sau ...

button.addEventListener( 'click', () => {
    alert( 'only once!' );
}, { once: true } );

https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Parameters


Làm mát cho đến khi bạn phát hiện ra rằng không có phiên bản IE hoặc edge <16 nào thực sự hỗ trợ tính năng này. Ít nhất trong 5 năm nữa, chúng ta có thể sử dụng cái này vì IE sẽ (đọc là: nên) không còn được dùng nữa, Edge sẽ thay thế và nó sẽ sử dụng webkit engine thay vì "EdgeHTML" của riêng họ.
SidOfc

1
với polyfill này cho DOM Level 4 mục bạn cần sử dụng tốt npmjs.com/package/dom4
shunryu111

2

Một tùy chọn không quá ẩn danh

element.funky = function() {
    console.log("Click!");
};
element.funky.type = "click";
element.funky.capt = false;
element.addEventListener(element.funky.type, element.funky, element.funky.capt);
// blah blah blah
element.removeEventListener(element.funky.type, element.funky, element.funky.capt);

Vì nhận được phản hồi từ Andy ( khá đúng, nhưng cũng như nhiều ví dụ, tôi muốn thể hiện ý tưởng mở rộng theo ngữ cảnh ), đây là một giải thích ít phức tạp hơn :

<script id="konami" type="text/javascript" async>
    var konami = {
        ptrn: "38,38,40,40,37,39,37,39,66,65",
        kl: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
    };
    document.body.addEventListener( "keyup", function knm ( evt ) {
        konami.kl = konami.kl.slice( -9 );
        konami.kl.push( evt.keyCode );
        if ( konami.ptrn === konami.kl.join() ) {
            evt.target.removeEventListener( "keyup", knm, false );

            /* Although at this point we wish to remove a listener
               we could easily have had multiple "keyup" listeners
               each triggering different functions, so we MUST
               say which function we no longer wish to trigger
               rather than which listener we wish to remove.

               Normal scoping will apply to where we can mention this function
               and thus, where we can remove the listener set to trigger it. */

            document.body.classList.add( "konami" );
        }
    }, false );
    document.body.removeChild( document.getElementById( "konami" ) );
</script>

Điều này cho phép cấu trúc hàm ẩn danh hiệu quả, tránh việc sử dụng callee thực tế không được dùng nữa và cho phép dễ dàng loại bỏ.

Ngẫu nhiên : Việc loại bỏ phần tử script ngay sau khi thiết lập trình nghe là một thủ thuật dễ thương để ẩn mã mà một người không muốn là rõ ràng đối với những cặp mắt tò mò ( sẽ làm hỏng sự ngạc nhiên ;-)

Vì vậy, phương pháp ( đơn giản hơn ) là:

element.addEventListener( action, function name () {
    doSomething();
    element.removeEventListener( action, name, capture );
}, capture );

2
Điều này là quá phức tạp.
Ben Sinclair

@Andy Tôi đồng ý, đại loại là, nhưng đang cố gắng cho thấy rằng đơn giản là không có cách nào để xóa một chức năng ẩn danh. Nó phải bằng cách nào đó được tham chiếu (thậm chí callee (là xấu, M'Kay) là tham khảo các chức năng), và do đó cung cấp một ví dụ về chỉ là một cách (khác) các chức năng có thể được tham chiếu - và, làm thế nào nó được xây dựng bộ phận đó đều có thể được lưu trữ để tham khảo sau này (phần quan trọng). Rõ ràng là một chức năng ẩn danh thực sự được xây dựng một cách nhanh chóng, và do đó, sau này biết được hành động / loại sự kiện nào và liệu việc chụp có được sử dụng hay không cũng phải được biết. Dù sao, đây là một phương pháp tốt hơn :-)
Fred Gandt

Làm việc hoàn hảo cho tôi. Tôi không thể thấy cách khác để chuyển các đối số vào hàm, vì nó không thể ẩn danh.
nicodemus13,

2

Điều này không lý tưởng vì nó loại bỏ tất cả, nhưng có thể phù hợp với nhu cầu của bạn:

z = document.querySelector('video');
z.parentNode.replaceChild(z.cloneNode(1), z);

Sao chép một nút sẽ sao chép tất cả các thuộc tính và giá trị của chúng, bao gồm cả các trình nghe nội tại (trong dòng). Nó không sao chép trình nghe sự kiện được thêm bằng addEventListener ()

Node.cloneNode ()


Điều này hoàn toàn tuyệt vời
Ahmad Alfy

1

JavaScript : Phương thức addEventListener đăng ký trình lắng nghe được chỉ định trên EventTarget (Phần tử | tài liệu | Cửa sổ) mà nó được gọi.

EventTarget. addEventListener ( event_type , handler_ function , Bubbling | Capturing );

Sự kiện chuột, bàn phím Kiểm tra ví dụ trong WebConsole:

var keyboard = function(e) {
    console.log('Key_Down Code : ' + e.keyCode);
};
var mouseSimple = function(e) {
    var element = e.srcElement || e.target;
    var tagName = element.tagName || element.relatedTarget;
    console.log('Mouse Over TagName : ' + tagName);    
};
var  mouseComplex = function(e) {
    console.log('Mouse Click Code : ' + e.button);
} 

window.document.addEventListener('keydown',   keyboard,      false);
window.document.addEventListener('mouseover', mouseSimple,   false);
window.document.addEventListener('click',     mouseComplex,  false);

Phương thức removeEventListener loại bỏ trình xử lý sự kiện đã đăng ký trước đó với EventTarget.addEventListener ().

window.document.removeEventListener('keydown',   keyboard,     false);
window.document.removeEventListener('mouseover', mouseSimple,  false);
window.document.removeEventListener('click',     mouseComplex, false);

caniuse


1

Để đưa ra cách tiếp cận cập nhật hơn cho vấn đề này:

//one-time fire
element.addEventListener('mousedown', {
  handleEvent: function (evt) {
    element.removeEventListener(evt.type, this, false);
  }
}, false);

2
Một lời giải thích sẽ rất hay.
Poul Bak

1

Tôi đã gặp phải vấn đề tương tự và đây là giải pháp tốt nhất mà tôi có thể nhận được:

/*Adding the event listener (the 'mousemove' event, in this specific case)*/
element.onmousemove = function(event) {
    /*do your stuff*/
};
/*Removing the event listener*/
element.onmousemove = null;

Xin lưu ý rằng tôi chỉ kiểm tra điều này cho windowphần tử và cho 'mousemove'sự kiện, vì vậy có thể có một số vấn đề với cách tiếp cận này.


0

Có thể không phải là giải pháp tốt nhất về những gì bạn đang yêu cầu. Tôi vẫn chưa xác định được phương pháp hiệu quả để loại bỏ hàm ẩn danh được khai báo nội dòng với lời gọi trình xử lý sự kiện.

Cá nhân tôi sử dụng một biến để lưu trữ <target>và khai báo hàm bên ngoài lời gọi trình xử lý sự kiện, ví dụ:

const target = document.querySelector('<identifier>');

function myFunc(event) { function code; }

target.addEventListener('click', myFunc);

Sau đó, để xóa trình nghe:

target.removeEventListener('click', myFunc);

Không phải đề xuất hàng đầu mà bạn sẽ nhận được nhưng để loại bỏ các chức năng ẩn danh, giải pháp duy nhất mà tôi thấy hữu ích là xóa rồi thay thế phần tử HTML. Tôi chắc chắn phải có một phương pháp vani JS tốt hơn nhưng tôi chưa thấy nó.


0

Tôi biết đây là một chủ đề khá cũ, nhưng tôi nghĩ tôi có thể bỏ vào hai xu của mình cho những ai thấy nó hữu ích.

Tập lệnh (xin lỗi về các tên phương thức không phù hợp):

window.Listener = {
    _Active: [],
    remove: function(attached, on, callback, capture){
        for(var i = 0; i < this._Active.length; i++){
            var current = this._Active[i];
            if(current[0] === attached && current[1] === on && current[2] === callback){
                attached.removeEventListener(on, callback, (capture || false));
                return this._Active.splice(i, 1);
            }
        }
    }, removeAtIndex(i){
        if(this._Active[i]){
            var remove = this._Active[i];
            var attached = remove[0], on = remove[1], callback = remove[2];
            attached.removeEventListener(on, callback, false);
            return this._Active.splice(i, 1);
        }
    }, purge: function(){
        for(var i = 0; i < this._Active.length; i++){
            var current = this._Active[i];
            current[0].removeEventListener(current[1], current[2]);
            this._Active.splice(i, 1);
        }
    }, declare: function(attached, on, callback, capture){
        attached.addEventListener(on, callback, (capture || false));
        if(this._Active.push([attached, on, callback])){
            return this._Active.length - 1;
        }
    }
};

Và bạn có thể sử dụng nó như vậy:

// declare a new onclick listener attached to the document
var clickListener = Listener.declare(document, "click" function(e){
    // on click, remove the listener and log the clicked element
    console.log(e.target);
    Listener.removeAtIndex(clickListener);
});

// completely remove all active listeners 
// (at least, ones declared via the Listener object)
Listener.purge();

// works exactly like removeEventListener
Listener.remove(element, on, callback);

-4
window.document.onkeydown = function(){};

Tại sao không = không xác định? Badass thực sự.
Ben Sinclair

2
Điều này sẽ không xóa bất kỳ trình xử lý nào đã được đăng ký với addEventListener.
Luc125
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.