removeEventListener บนฟังก์ชันที่ไม่ระบุชื่อใน JavaScript


107

ฉันมีวัตถุที่มีวิธีการอยู่ในนั้น วิธีการเหล่านี้ใส่ลงในวัตถุภายในฟังก์ชันที่ไม่ระบุตัวตน ดูเหมือนว่า:

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

(มีรหัสมากกว่านี้มาก แต่ก็เพียงพอที่จะแสดงปัญหา)

ตอนนี้ฉันต้องการหยุดผู้ฟังเหตุการณ์ในบางกรณี ดังนั้นฉันจึงพยายามทำ removeEventListener แต่ฉันคิดไม่ออกว่าจะทำอย่างไร ฉันได้อ่านในคำถามอื่น ๆ ว่าไม่สามารถเรียก removeEventListener ในฟังก์ชันที่ไม่ระบุชื่อได้ แต่ในกรณีนี้ก็เป็นเช่นกันหรือไม่

ฉันมีวิธีการใน t ที่สร้างขึ้นภายในฟังก์ชันที่ไม่ระบุตัวตนดังนั้นฉันจึงคิดว่ามันเป็นไปได้ มีลักษณะดังนี้:

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

ทำไมฉันทำไม่ได้

มีวิธีอื่น (ที่ดี) ในการทำเช่นนี้หรือไม่?

ข้อมูลโบนัส; สิ่งนี้ต้องทำงานใน Safari เท่านั้นดังนั้นการรองรับ IE ที่ขาดหายไป


ทำไมไม่บันทึกฟังก์ชันนี้ ตัวจัดการเหตุการณ์อาจไม่ใช่ฟังก์ชันที่ไม่ระบุชื่อ
kirilloid

2
ฉันรู้ว่านี่ช้าไปหน่อย แต่คุณยังสามารถใช้เมธอด Node.setUserData /Node.getUserData เพื่อจัดเก็บข้อมูลเกี่ยวกับองค์ประกอบได้ ตัวอย่างเช่นเมื่อคุณต้องการตั้งค่า anon listener (และสามารถลบออกได้) ให้ตั้งค่า userdata เป็นฟังก์ชัน anon (Elem.setUserData('eventListener', function(e){console.log('Event fired.');}, null);ก่อนแล้วจึงทำ Elem.addEventListener ('event', Elem.getUserData ('eventListener'), false); ... และเหมือนกันสำหรับ removeEventListener หวังว่าคุณจะเห็นสิ่งนี้ดี
ไล่

แก้ไข: ตามความคิดเห็นก่อนหน้านี้ฉันเดาว่าใช้ได้เฉพาะใน Firefox ... ฉันเพิ่งลอง IE8 (ไม่ทราบ IE9), Safari 5.1.2, Chrome (?), Opera 11 .. ไม่มีลูกเต๋า
Chase

คำตอบ:


79

ฉันเชื่อว่านั่นเป็นจุดสำคัญของฟังก์ชันที่ไม่ระบุตัวตนมันไม่มีชื่อหรือวิธีอ้างอิง

ถ้าฉันเป็นคุณฉันจะสร้างฟังก์ชันที่มีชื่อหรือใส่ไว้ในตัวแปรเพื่อให้คุณมีข้อมูลอ้างอิง

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

จากนั้นคุณสามารถลบออกได้โดย

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

3
ขอบคุณสำหรับการตอบกลับของคุณ. ฉันไปกับ: var handler; window.document.addEventListener ("keydown", handler = function (e) {แต่สิ่งที่ฉันไม่เข้าใจคือทำไม "สิ่งนี้" ไม่อ้างอิงถึงตัวฟังเหตุการณ์สิ่งที่ฟังเหตุการณ์ควรเป็นวัตถุไม่ใช่หรือ
bitkid

1
thisคำหลักที่สามารถทำให้เกิดความสับสน สถานที่ที่ดีในการอ่านคือquirksmode.org/js/this.html
Adam Heath

ขอบคุณมาก. สิ่งนี้มีประโยชน์มากที่สุด
bitkid

ฉันกำลังพยายามทำสิ่งนี้เพื่อบล็อกโฆษณาที่คงอยู่บนเว็บไซต์ ฉันรู้ว่านี่คือจุดสำคัญของฟังก์ชันที่ไม่ระบุตัวตน แต่นั่นไม่ได้หมายความว่าฉันจะไม่อยากรู้วิธีการทำเช่นนั้น
Wyatt8740

@bitkid: ภายในฟังก์ชันตัวจัดการ (สมมติว่าไม่ใช่ฟังก์ชันลูกศร) thisหมายถึงองค์ประกอบที่มีการเพิ่ม Listener ไม่ใช่เหตุการณ์ (ซึ่งจะเป็นพารามิเตอร์e) ดังนั้นthis === e.currentTarget. อ่านdeveloper.mozilla.org/en-US/docs/Web/API/EventTarget/…
chharvey

100

หากคุณอยู่ในฟังก์ชันจริงคุณสามารถใช้ arguments.callee เป็นการอ้างอิงถึงฟังก์ชัน เช่นเดียวกับใน:

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

แก้ไข: สิ่งนี้จะไม่ทำงานหากคุณทำงานในโหมดเข้มงวด ( "use strict";)


2
นี่เป็นสิ่งที่ดีเนื่องจากรักษาข้อดีของฟังก์ชันที่ไม่ระบุตัวตนไว้ (ไม่ทำให้เนมสเปซเป็นมลพิษเป็นต้น)
bompf

3
ลองสิ่งนี้ในแอป WinJS ได้รับข้อผิดพลาดถัดไป: "ไม่อนุญาตให้เข้าถึงคุณสมบัติ 'callee' ของวัตถุอาร์กิวเมนต์ในโหมดเข้มงวด"
Valentin Kantor

1
@ValentinKantor: นั่นเป็นเพราะบางอย่างในรหัสมี "ใช้อย่างเข้มงวด"; คำสั่งและคุณไม่สามารถใช้ callee ในโหมดเข้มงวด
OMA

20
ตั้งชื่อฟังก์ชันแบบอินไลน์และคุณสามารถอ้างอิงได้โดยไม่ต้องอาศัยอาร์กิวเมนต์ callee:button.addEventListener('click', function handler() { this.removeEventListener('click', handler); });
Harry Love

4
ตามที่ระบุไว้ใน Mozilla: "คำเตือน: ECMAScript (ES5) รุ่นที่ 5 ห้ามการใช้ arguments.callee () ในโหมดที่เข้มงวดหลีกเลี่ยงการใช้ arguments.callee () โดยให้ชื่อนิพจน์ฟังก์ชันหรือใช้การประกาศฟังก์ชันโดยที่ฟังก์ชัน ต้องเรียกเอง”
เพื่อน

53

โซลูชันของOtto Nascarellaเวอร์ชันที่ทำงานในโหมดเข้มงวดคือ:

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

4
ทางออกสวย!
Eric Norcross

2
นี่อาจไม่ใช่วิธีที่ถูกต้อง แต่นี่เป็นวิธีที่ง่ายที่สุด
Vignesh

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

อาจเป็นฟังก์ชั่นที่ไม่ระบุตัวตนหลายอย่างคีย์ดาวน์1

คำเตือน: ใช้ได้เฉพาะในChrome Dev Tools& ไม่สามารถใช้ใน code : link


2
ขอบคุณคุณได้แก้ปริศนาแล้วอย่างน้อยก็ใน Chrome สำหรับสิ่งที่นักเลงหลายคนบอกว่าเป็นไปไม่ได้ ผู้ชายคุณก็เหมือน ... แบทแมน!
JasonXA

21
getEventListenersดูเหมือนจะเป็นส่วนหนึ่งของ Chrome Dev-tools ดังนั้นจึงไม่สามารถใช้กับสิ่งอื่นใดได้นอกจากการดีบัก
DBS

1
เพิ่งลองใช้งานได้รับการยืนยันว่ามีเฉพาะใน Devtools เท่านั้นและไม่รวมอยู่ในสคริปต์ภายในเพจ
Andres Riofrio

5

ในเบราว์เซอร์สมัยใหม่คุณสามารถทำสิ่งต่อไปนี้ ...

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

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


เจ๋งจนกว่าคุณจะพบว่าไม่มีเวอร์ชันของ IE หรือ edge <16 ที่รองรับคุณสมบัตินี้จริงๆ อย่างน้อยใน 5 ปีเราสามารถใช้สิ่งนี้ได้ตั้งแต่นั้น IE จะ (อ่าน: ควร) จะเลิกใช้งาน Edge จะเข้ามาแทนที่และจะใช้ webkit engine แทนสิ่งที่ "EdgeHTML" ของตัวเอง
SidOfc

1
ด้วย polyfill นี้สำหรับรายการ DOM ระดับ 4 คุณควรจะสบายดีnpmjs.com/package/dom4
shunryu111

2

ตัวเลือกที่ไม่ระบุตัวตน

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 ( ค่อนข้างถูกต้อง แต่เช่นเดียวกับหลาย ๆ ตัวอย่างฉันต้องการแสดงการขยายความคิดตามบริบท ) นี่เป็นคำอธิบายที่ซับซ้อนน้อยกว่า :

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

นี้จะช่วยให้โครงสร้างฟังก์ชั่นที่ไม่ระบุชื่อได้อย่างมีประสิทธิภาพหลีกเลี่ยงการใช้ของที่เลิกจริงcalleeและช่วยให้กำจัดง่าย

อนึ่ง : การลบองค์ประกอบสคริปต์ทันทีหลังจากตั้งค่าฟังเป็นเคล็ดลับที่น่ารักสำหรับการซ่อนรหัสที่ใคร ๆ ก็ไม่ต้องการอย่างชัดเจนในการสอดรู้สอดเห็น ( จะทำให้เสียความประหลาดใจ ;-)

ดังนั้นวิธีการ ( ง่ายกว่านั้น) คือ:

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

2
มันซับซ้อนเกินไป
pronebird

@ ฉันเห็นด้วย แต่พยายามแสดงให้เห็นว่าไม่มีวิธีใดที่จะลบฟังก์ชันที่ไม่ระบุตัวตนได้ มันต้องในบางวิธีจะอ้างอิง (แม้จะถูกเรียก (ไม่ดี M'Kay) จะอ้างอิงถึงฟังก์ชั่น) จึงจัดให้มีตัวอย่างหนึ่ง (อื่น ๆ ) เพียงวิธีการทำงานที่สามารถอ้างอิง - และวิธีการที่จะสร้างขึ้นจากชิ้นส่วนที่ สามารถจัดเก็บไว้สำหรับการอ้างอิงในภายหลังได้อย่างเท่าเทียมกัน (ส่วนที่สำคัญ) เห็นได้ชัดว่าฟังก์ชั่นที่ไม่ระบุตัวตนอย่างแท้จริงนั้นค่อนข้างถูกสร้างขึ้นในทันทีดังนั้นการทราบในภายหลังว่าการกระทำ / ประเภทของเหตุการณ์ใดและต้องทราบว่ามีการใช้การจับภาพหรือไม่ อย่างไรก็ตามนี่เป็นวิธีที่ดีกว่า :-)
Fred Gandt

ทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน ฉันไม่เห็นวิธีอื่นในการส่งผ่านอาร์กิวเมนต์ไปยังฟังก์ชันเนื่องจากไม่สามารถระบุตัวตนได้
nicodemus13

2

สิ่งนี้ไม่เหมาะอย่างยิ่งเนื่องจากจะลบทั้งหมด แต่อาจเหมาะกับความต้องการของคุณ:

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

การโคลนโหนดจะคัดลอกแอตทริบิวต์และค่าทั้งหมดของโหนดรวมถึงอินทรินซิค (ในบรรทัด) ไม่คัดลอกตัวฟังเหตุการณ์ที่เพิ่มโดยใช้ addEventListener ()

Node.cloneNode ()


นี่เป็นสิ่งที่ยอดเยี่ยมอย่างแน่นอน
Ahmad Alfy

1

JavaScript : เมธอดaddEventListener จะลงทะเบียนตัวฟังที่ระบุบน EventTarget (Element | document | Window) ที่เรียกใช้

EventTarget addEventListener ( event_type , handler_function, Bubbling | Capturing );

ตัวอย่างเหตุการณ์เมาส์แป้นพิมพ์ใน 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);

เมธอดremoveEventListener จะลบตัวฟังเหตุการณ์ที่ลงทะเบียนไว้ก่อนหน้านี้กับ EventTarget.addEventListener ()

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

ฉันสามารถใช้


1

เพื่อให้แนวทางที่เป็นปัจจุบันมากขึ้น:

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

3
คำอธิบายจะดี
Poul Bak

1

ฉันเจอปัญหาเดียวกันและนี่คือทางออกที่ดีที่สุดที่ฉันจะได้รับ:

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

โปรดทราบว่าฉันได้ทดสอบสิ่งนี้สำหรับwindowองค์ประกอบและสำหรับ'mousemove'เหตุการณ์เท่านั้นดังนั้นวิธีนี้อาจมีปัญหา


0

อาจไม่ใช่ทางออกที่ดีที่สุดในแง่ของสิ่งที่คุณกำลังถาม ฉันยังไม่ได้กำหนดวิธีการที่มีประสิทธิภาพในการลบฟังก์ชันที่ไม่ระบุตัวตนที่ประกาศในบรรทัดพร้อมกับการเรียกตัวฟังเหตุการณ์

ฉันใช้ตัวแปรเป็นการส่วนตัวเพื่อจัดเก็บ<target>และประกาศฟังก์ชันภายนอกการเรียกตัวฟังเหตุการณ์เช่น:

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

function myFunc(event) { function code; }

target.addEventListener('click', myFunc);

จากนั้นในการลบผู้ฟัง:

target.removeEventListener('click', myFunc);

ไม่ใช่คำแนะนำยอดนิยมที่คุณจะได้รับ แต่ในการลบฟังก์ชันที่ไม่ระบุตัวตนทางออกเดียวที่ฉันพบว่ามีประโยชน์คือการลบแล้วแทนที่องค์ประกอบ HTML ฉันแน่ใจว่าต้องมีวิธี vanilla JS ที่ดีกว่านี้ แต่ฉันยังไม่เห็น


0

ฉันรู้ว่านี่เป็นกระทู้ที่ค่อนข้างเก่า แต่คิดว่าฉันอาจใส่สองเซ็นต์สำหรับคนที่คิดว่ามีประโยชน์

สคริปต์ (ขออภัยเกี่ยวกับชื่อเมธอดที่ไม่สร้างสรรค์):

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

และคุณสามารถใช้มันดังนี้:

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

ฉันเพิ่งประสบปัญหาคล้ายกับปลั๊กอิน wordpress ป้องกันการคัดลอก รหัสคือ:

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

จากนั้นก็เริ่มต้นด้วยการใส่อย่างหลวม ๆ

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

ฉันทำสิ่งนี้ได้ง่ายๆโดยการแนบฟังก์ชั่นที่ไม่ระบุชื่ออื่น ๆ เข้ากับเหตุการณ์นี้:

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

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

ทำไมไม่ = ไม่ได้กำหนด? เหี้ยจริง.
pronebird

2
นี้จะไม่นำการจัดการใด ๆ addEventListenerที่ได้รับการจดทะเบียนกับ
Luc125
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.