JavaScript'teki anonim işlevler hakkında removeEventListener


107

İçinde yöntemleri olan bir nesnem var. Bu yöntemler, anonim bir işlevin içindeki nesneye yerleştirilir. Şöyle görünüyor:

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);
    };
});  

(çok daha fazla kod var, ancak bu sorunu göstermek için yeterli)

Şimdi bazı durumlarda olay dinleyicisini durdurmak istiyorum. Bu nedenle bir removeEventListener yapmaya çalışıyorum ama bunu nasıl yapacağımı bulamıyorum. Diğer sorularda, removeEventListener'ı anonim işlevlerde çağırmanın mümkün olmadığını okudum, ancak bu durumda da durum bu mu?

Anonim işlevin içinde oluşturulmuş bir yöntemim var ve bu nedenle bunun mümkün olduğunu düşündüm. Buna benzer:

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

Bunu neden yapamıyorum?

Bunu yapmanın başka (iyi) bir yolu var mı?

Bonus bilgisi; bu sadece Safari'de çalışmak zorunda, dolayısıyla IE desteği eksik.


Neden bu işlevi kaydetmiyorsunuz? Olay işleyici anonim bir işlev olmayabilir.
kirilloid

2
Bunun biraz geç olduğunun farkındayım, ancak bir öğe hakkındaki verileri depolamak için Node.setUserData /Node.getUserData yöntemlerini de kullanabilirsiniz . Örneğin, bir anon dinleyici ayarlamanız (ve onu kaldırabilmeniz) gerektiğinde, önce kullanıcı verilerini bir anon işlevine ayarlayın (Elem.setUserData('eventListener', function(e){console.log('Event fired.');}, null);ve ardından Elem.addEventListener ('event', Elem.getUserData ('eventListener'), false) yapın; ... ve removeEventListener için de aynı. Umarım bunu iyi görebilirsin.
Chase

DÜZENLEME: Önceki yoruma göre, sanırım bu yalnızca Firefox'ta çalışıyor ... IE8 (IE9 bilinmiyor), Safari 5.1.2, Chrome (?), Opera 11 ... Dice yok
Chase

Yanıtlar:


79

Bunun anonim bir işlevin amacı olduğuna inanıyorum, bir adı veya ona gönderme yapma yolu yok.

Senin yerinde olsam, sadece adlandırılmış bir fonksiyon yaratırdım veya onu bir değişkene koyardım, böylece ona bir referansınız olur.

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);

Daha sonra bunu kaldırabilirsiniz

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

3
Cevabın için teşekkürler. Şununla gittim: var handler; window.document.addEventListener ("keydown", handler = function (e) {Ama anlamadığım şey, "this" in olay dinleyicisine neden
başvurmadığı

1
thisAnahtar kelime kafa karıştırıcı olabilir. Okumak için iyi bir yer quirksmode.org/js/this.html
Adam Heath

Çok teşekkür ederim. Bu çok yardımcı oldu.
bitkid

Bunu bir web sitesinde gerçekten kalıcı bir reklamı engellemek için yapmaya çalışıyorum. Bunun anonim işlevlerin amacı olduğunu biliyorum, ancak bu nasıl yapılacağını bilmek istemediğim anlamına gelmez.
Wyatt8740

@bitkid: Bir işleyici işlevinin içinde (bunun bir ok işlevi olmadığı varsayılarak), thisolayın kendisini değil (parametre olacaktır e) dinleyicinin eklendiği öğeyi ifade eder . Bu nedenle this === e.currentTarget. okuma developer.mozilla.org/en-US/docs/Web/API/EventTarget/...
chharvey

100

gerçek işlevin içindeyseniz, arguments.callee'yi işleve bir başvuru olarak kullanabilirsiniz. de olduğu gibi:

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

DÜZENLEME: Katı modda ( "use strict";) çalışıyorsanız bu çalışmayacaktır


2
Bu, anonim işlevlerin avantajlarını koruduğu için (ad alanını kirletmeyen vb.) Güzel.
bompf

3
bunu WinJS uygulamasında denedi, bir sonraki hatayı aldım: "Bir bağımsız değişken nesnesinin 'aranan' özelliğine katı modda erişilmesine izin verilmez"
Valentin Kantor

1
@ValentinKantor: Çünkü koddaki bir şey "sıkı kullanım"; deyimi ve aranan ucu katı modda kullanamazsınız.
OMA

20
Satır içi işleve bir ad verin ve argümanlara başvurmadan ona başvurabilirsiniz. Callee:button.addEventListener('click', function handler() { this.removeEventListener('click', handler); });
Harry Love

4
Mozilla'da belirtildiği gibi: "Uyarı: ECMAScript'in (ES5) 5. sürümü, katı modda arguments.callee () 'nin kullanılmasını yasaklar. İşlev ifadelerine bir ad vererek arguments.callee () kullanmaktan kaçının veya bir işlev kendisini aramalı. "
ahbap

53

Otto Nascarella'nın katı modda çalışan çözümünün bir versiyonu :

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

4
Güzel çözüm!
Eric Norcross

2
Bu doğru yol olmayabilir ama bu en kolay şey.
Vignesh

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

Birkaç anonim işlev olabilir, tuşa basma 1

Uyarı: yalnızca içinde çalışır Chrome Dev Toolsve kodda kullanılamaz : bağlantı


2
Teşekkürler, pek çok şakacının imkansız olduğunu söylediği için, en azından Chrome'da bir bilmeceyi çözdünüz. Adamım, sen ... Batman gibisin!
JasonXA

21
getEventListenersChrome Dev-araçlarının bir parçası gibi görünüyor, bu nedenle hata ayıklama dışında hiçbir şey için gerçekten kullanılamaz.
DBS

1
Yeni denedim, yalnızca Devtools'da mevcut olduğunu ve bir sayfadaki bir komut dosyasına dahil edildiğinde olmadığını doğruladı.
Andres Riofrio

5

modern tarayıcılarda şunları yapabilirsiniz ...

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

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


Hiçbir IE veya edge <16 sürümünün bu özelliği gerçekten desteklemediğini öğrenene kadar soğutun. En azından 5 yıl içinde bunu kullanabiliriz, o zamandan beri IE (okuyun: gerekir) kullanımdan kaldırılacak, Edge yerini alacak ve kendi "EdgeHTML" yerine webkit motorunu kullanacak.
SidOfc

1
DOM Seviye 4 girişleri için bu polyfill ile iyi olmalısınız npmjs.com/package/dom4
shunryu111

2

Çok anonim olmayan bir seçenek

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);

Andy'den geri bildirim aldığım için ( oldukça doğru, ancak birçok örnekte olduğu gibi, fikrin bağlamsal bir genişlemesini göstermek istedim ), işte daha az karmaşık bir açıklama:

<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>

Bu, etkili bir şekilde anonim bir işlev yapısına izin verir, pratik olarak kullanımdan kaldırılan aranan ucun kullanılmasını önler ve kolayca çıkarılmasına izin verir.

Bu arada : Dinleyiciyi ayarladıktan hemen sonra komut dosyası öğesinin kaldırılması, birinin tercih edeceği kodu gizlemek için sevimli bir hile, meraklı gözler için açıkça açık değildi ( sürprizi bozardı ;-)

Dolayısıyla yöntem ( daha basit ):

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

2
Bu çok karmaşık.
pronebird

@Andy Katılıyorum, sayılır, ancak anonim bir işlevi kaldırmanın bir yolu olmadığını göstermeye çalışıyordum. Bu olmalı bir şekilde başvurulan (hatta Aranan (kötü, T'mam) işlevini başvurduğunu) ve dolayısıyla sağlanan sadece bir (başka) yolun bir örneğini işlevi başvurulabilir - bu parçaların inşa nasıl ve bu daha sonra başvurmak üzere eşit olarak saklanabilir (önemli kısım). Açıkçası, gerçekten anonim bir işlev bir şekilde anında inşa edilir ve bu nedenle daha sonra hangi olay eylemi / türü ve yakalamanın kullanılıp kullanılmadığını bilmek de bilinmelidir. Her neyse, işte daha iyi bir yöntem :-)
Fred Gandt

Benim için mükemmel çalıştı. Anonim olamayacağı için argümanları işleve geçirmenin başka bir yolunu göremedim.
nicodemus13

2

Bu, hepsini ortadan kaldırdığı için ideal değildir, ancak ihtiyaçlarınız için işe yarayabilir:

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

Bir düğümü klonlamak, iç (sıralı) dinleyiciler dahil olmak üzere tüm özniteliklerini ve değerlerini kopyalar. AddEventListener () kullanılarak eklenen olay dinleyicilerini kopyalamaz

Node.cloneNode ()


Bu kesinlikle harika
Ahmad Alfy

1

JavaScript : addEventListener yöntemi, belirtilen dinleyiciyi çağrıldığı EventTarget (Öğe | belge | Pencere) üzerinde kaydeder.

EventTarget. addEventListener ( event_type , handler_function, Bubbling | Yakalama );

Fare, Klavye olayları WebConsole'da örnek test:

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);

removeEventListener yöntemi, EventTarget.addEventListener () ile önceden kaydedilmiş olay dinleyicisini kaldırır.

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

kullanabilirmiyim


1

Buna daha güncel bir yaklaşım vermek için:

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

3
Bir açıklama güzel olurdu.
Poul Bak

1

Aynı soruna rastladım ve alabileceğim en iyi çözüm buydu:

/*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;

Lütfen bunu yalnızca windowöğe ve 'mousemove'olay için test ettiğimi unutmayın , bu nedenle bu yaklaşımla ilgili bazı sorunlar olabilir.


0

Muhtemelen ne istediğiniz açısından en iyi çözüm değil. Olay dinleyicisi çağrısı ile satır içi olarak bildirilen anonim işlevi kaldırmak için hala verimli bir yöntem belirlemedim.

Ben şahsen <target>, işlevi olay dinleyici çağrısı dışında depolamak ve bildirmek için bir değişken kullanıyorum, örneğin:

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

function myFunc(event) { function code; }

target.addEventListener('click', myFunc);

Ardından dinleyiciyi kaldırmak için:

target.removeEventListener('click', myFunc);

Alacağınız en önemli öneri değil, ancak anonim işlevleri kaldırmak için yararlı bulduğum tek çözüm, HTML öğesini kaldırıp ardından değiştirmektir. Eminim daha iyi bir vanilya JS yöntemi olmalı ama henüz görmedim.


0

Bunun oldukça eski bir konu olduğunu biliyorum, ancak yararlı bulanlar için iki sentimi koyabileceğimi düşündüm.

Komut dosyası (yaratıcı olmayan yöntem adları için özür dileriz):

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;
        }
    }
};

Ve bunu şu şekilde kullanabilirsiniz:

// 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);

-1

Kopya korumalı wordpress eklentisiyle ilgili benzer bir sorun yaşadım. Kod şuydu:

function disableSelection(target){
 if (typeof target.onselectstart!="undefined") //For IE 
  target.onselectstart=function(){return false}
 else if (typeof target.style.MozUserSelect!="undefined") //For Firefox
  target.style.MozUserSelect="none"
 else //All other route (For Opera)
  target.onmousedown=function(){return false}
target.style.cursor = "default"
}

Ve sonra gevşek bir şekilde yerleştirilerek başlatıldı

<script type="text/javascript">disableSelection(document.body)</script>.

Bu olaya başka anonim işlevi ekleyerek geldim:

document.body.onselectstart = function() { return true; };

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

Neden değil = tanımsız? Gerçek baş belası.
pronebird

2
Bu, kayıtlı herhangi bir işleyiciyi kaldırmaz addEventListener.
Luc125
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.