Javascript / DOM: จะลบเหตุการณ์ทั้งหมดของวัตถุ DOM ได้อย่างไร


97

คำถาม: มีวิธีใดในการลบเหตุการณ์ทั้งหมดของวัตถุเช่น div หรือไม่?

แก้ไข: ฉันกำลังเพิ่มต่อdiv.addEventListener('click',eventReturner(),false);เหตุการณ์

function eventReturner() {
    return function() {
        dosomething();
    };
}

แก้ไข 2: ฉันพบวิธีที่ใช้งานได้ แต่ไม่สามารถใช้กับกรณีของฉันได้:

var returnedFunction;
function addit() {
    var div = document.getElementById('div');
    returnedFunction = eventReturner();
    div.addEventListener('click',returnedFunction,false); //You HAVE to take here a var and not the direct call to eventReturner(), because the function address must be the same, and it would change, if the function was called again.
}
function removeit() {
    var div = document.getElementById('div');
    div.removeEventListener('click',returnedFunction,false);
}

คุณแนบเหตุการณ์อย่างไร
Felix Kling

2
ชื่อเรื่องถามเกี่ยวกับองค์ประกอบของวัตถุในขณะที่คำถามที่เกิดขึ้นจริงถามเกี่ยวกับเหตุการณ์ที่เกิดขึ้น คุณต้องการลบองค์ประกอบย่อยหรือเหตุการณ์?
Pär Wieslander

โอ้ d * mn นั่นเป็นสาเหตุที่ฉันคิดอย่างอื่นเมื่อฉันเขียนว่า ... ฉันห่วงใยเหตุการณ์ที่เกิดขึ้น
Florian Müller

ฉันไม่ทราบกรณีที่แน่นอน แต่ในบางกรณีเป็นวิธีแก้ปัญหาคุณสามารถใช้เมธอด 'on *' (เป็น div.onclick = function) ซึ่งใช้งานได้กับตัวฟังเดียวเสมอและง่ายต่อการลบเช่นdiv.onclick=nullกัน แน่นอนว่าคุณไม่ควรใช้addEventListenerในกรณีนี้โดยสิ้นเชิงเพราะมันจะเพิ่มตัวฟังแยกต่างหากที่แตกต่างจากในonclick.
venimus

คำตอบ:


106

ผมไม่แน่ใจว่าสิ่งที่คุณหมายกับลบทุกเหตุการณ์ ลบตัวจัดการทั้งหมดสำหรับเหตุการณ์บางประเภทหรือตัวจัดการเหตุการณ์ทั้งหมดสำหรับประเภทเดียวหรือไม่

ลบตัวจัดการเหตุการณ์ทั้งหมด

หากคุณต้องการลบตัวจัดการเหตุการณ์ทั้งหมด (ประเภทใดก็ได้) คุณสามารถโคลนองค์ประกอบและแทนที่ด้วยโคลนของมัน:

var clone = element.cloneNode(true);

หมายเหตุ:สิ่งนี้จะเก็บรักษาแอตทริบิวต์และลูก ๆ ไว้ แต่จะไม่รักษาการเปลี่ยนแปลงใด ๆ กับคุณสมบัติ DOM


ลบตัวจัดการเหตุการณ์ "ไม่ระบุตัวตน" บางประเภท

วิธีอื่นคือการใช้removeEventListener()แต่ฉันเดาว่าคุณลองแล้วและไม่ได้ผล นี่คือสิ่งที่จับได้ :

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

คุณกำลังส่งฟังก์ชันที่ไม่ระบุตัวตนaddEventListenerเป็นหลักเพื่อeventReturnerส่งกลับฟังก์ชัน

คุณมีความเป็นไปได้สองประการในการแก้ปัญหานี้:

  1. อย่าใช้ฟังก์ชันที่ส่งคืนฟังก์ชัน ใช้ฟังก์ชั่นโดยตรง:

    function handler() {
        dosomething();
    }
    
    div.addEventListener('click',handler,false);
    
  2. สร้างกระดาษห่อหุ้มเพื่อaddEventListenerเก็บข้อมูลอ้างอิงไปยังฟังก์ชันที่ส่งคืนและสร้างremoveAllEventsฟังก์ชันแปลก ๆ:

    var _eventHandlers = {}; // somewhere global
    
    const addListener = (node, event, handler, capture = false) => {
      if (!(event in _eventHandlers)) {
        _eventHandlers[event] = []
      }
      // here we track the events and their nodes (note that we cannot
      // use node as Object keys, as they'd get coerced into a string
      _eventHandlers[event].push({ node: node, handler: handler, capture: capture })
      node.addEventListener(event, handler, capture)
    }
    
    const removeAllListeners = (targetNode, event) => {
      // remove listeners from the matching nodes
      _eventHandlers[event]
        .filter(({ node }) => node === targetNode)
        .forEach(({ node, handler, capture }) => node.removeEventListener(event, handler, capture))
    
      // update _eventHandlers global
      _eventHandlers[event] = _eventHandlers[event].filter(
        ({ node }) => node !== targetNode,
      )
    }
    

    จากนั้นคุณสามารถใช้กับ:

    addListener(div, 'click', eventReturner(), false)
    // and later
    removeAllListeners(div, 'click')
    

การสาธิต

หมายเหตุ:หากโค้ดของคุณทำงานเป็นเวลานานและคุณกำลังสร้างและลบองค์ประกอบจำนวนมากคุณจะต้องแน่ใจว่าได้ลบองค์ประกอบที่มีอยู่_eventHandlersเมื่อคุณทำลายมัน


@ ฟลอริดา: แน่นอนว่าโค้ดสามารถปรับปรุงได้ แต่ควรให้ความคิดว่า ... มีความสุขในการเข้ารหัส!
Felix Kling

2
คุณลองสิ่งนี้กับองค์ประกอบ div มากกว่าหนึ่งรายการแล้วหรือยัง จริงๆแล้วโหนด var จะถูกแปลงเป็นสตริงเช่น "[object HTMLDivElement]" ซึ่งหมายความว่าคุณต้องเพิ่มทุกอย่างในโหนดเดียวกัน
cstruter

@cstruter: ใช่ ... นี่เป็นช่วงแรก ๆ ของ JavaScript ... ฉันจะแก้ไขโค้ดเมื่อมีเวลามากขึ้น ขอบคุณสำหรับการให้ฉันรู้ว่า.
Felix Kling

ฉันเดาว่ามีการพิมพ์ผิด addListener ควรเป็น addEvent
AGamePlayer

โปรดทราบว่าelement.parentNodeอาจเป็นค่าว่าง ควรตรวจสอบ if รอบ ๆ ส่วนของโค้ดด้านบน
thecoolmacdude

42

การดำเนินการนี้จะลบผู้ฟังทั้งหมดออกจากระดับย่อย แต่จะช้าสำหรับเพจขนาดใหญ่ เขียนง่ายสุด ๆ

element.outerHTML = element.outerHTML;

2
คำตอบที่ดี สำหรับโหนดใบไม้นี่เป็นความคิดที่ดีที่สุด
user3055938

3
document.querySelector('body').outerHTML = document.querySelector('body').outerHTMLทำงานให้ฉัน
Ryan

2
@Ryan แทนคุณdocument.querySelector('body').outerHTMLสามารถพิมพ์ได้document.body.outerHTML
Jay Dadhania

28

remove()ใช้ฟังก์ชั่นของตัวเองฟังเหตุการณ์ ตัวอย่างเช่น:

getEventListeners().click.forEach((e)=>{e.remove()})

2
ไม่แน่ใจว่าเหตุใดจึงไม่ใช่คำตอบที่ถูกต้อง คำถามปรากฏขึ้นหลายครั้งใน SO และคำตอบอาจจัดการกับปัญหาโดยใช้วิธีโคลน
jimasun

42
ดูเหมือนว่าgetEventListenersจะพร้อมใช้งานในเครื่องมือ WebKit dev และใน FireBug เท่านั้น ไม่ใช่สิ่งที่คุณสามารถเรียกได้ในรหัสของคุณ
corwin.amber

1
getEventListeners()มีปัญหาในการนี้เพื่อให้การทำงานของคุณผ่านวัตถุเป็นอาร์กิวเมนต์ไปยัง ตัวอย่าง: getEventListeners(document)หรือgetEventListeners(document.querySelector('.someclass'))
ลุค

10
ดูเหมือนว่าจะใช้ได้กับคอนโซล Chrome เท่านั้น แม้แต่ในสคริปต์ของคุณในหน้าก็ไม่ทำงาน
Reuel Ribeiro

4
getEventListeners(document).click.forEach((e)=>{e.remove()})ทำให้เกิดข้อผิดพลาดนี้:VM214:1 Uncaught TypeError: e.remove is not a function
Ryan

11

ดังที่ corwin.amber กล่าวว่ามีความแตกต่างระหว่าง Webkit และอื่น ๆ

ใน Chrome:

getEventListeners(document);

ซึ่งให้ Object กับผู้ฟังเหตุการณ์ที่มีอยู่ทั้งหมด:

Object 
 click: Array[1]
 closePopups: Array[1]
 keyup: Array[1]
 mouseout: Array[1]
 mouseover: Array[1]
 ...

จากที่นี่คุณสามารถเข้าถึงผู้ฟังที่คุณต้องการลบ:

getEventListeners(document).copy[0].remove();

ดังนั้นผู้ฟังเหตุการณ์ทั้งหมด:

for(var eventType in getEventListeners(document)) {
   getEventListeners(document)[eventType].forEach(
      function(o) { o.remove(); }
   ) 
}

ใน Firefox

แตกต่างกันเล็กน้อยเนื่องจากใช้ Listener wrapper ที่มีฟังก์ชัน no remove คุณต้องรับฟังที่คุณต้องการลบ:

document.removeEventListener("copy", getEventListeners(document).copy[0].listener)

ผู้ฟังเหตุการณ์ทั้งหมด:

for(var eventType in getEventListeners(document)) {
  getEventListeners(document)[eventType].forEach(
    function(o) { document.removeEventListener(eventType, o.listener) }
  ) 
}

ฉันสะดุดกับโพสต์นี้ที่พยายามปิดการป้องกันการคัดลอกที่น่ารำคาญของเว็บไซต์ข่าว

สนุก!


นี่คือคำตอบที่ดีที่สุดสำหรับการกำหนดเป้าหมายการลบผู้ฟังเช่นในกรณีที่ลบตัวฟังที่เฉพาะเจาะจงบนโหนดเอกสารออกโดยไม่ต้องลบผู้ฟังทั้งหมดออก
Trevor

คำตอบนี้ใช้ได้ดีและดีที่สุด @Jmakuc ขอบคุณมาก.
Thavamani Kasi

5
Firefox กำลังบอกฉันgetEventListenersว่าไม่ได้กำหนดไว้?
rovyko

นี่เป็นวิธีแก้ปัญหาที่ไม่ดีเนื่องจากในปัจจุบัน getEventListeners รองรับเฉพาะ Chrome
Michael Paccione

1

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

if (EventTarget.prototype.original_addEventListener == null) {
    EventTarget.prototype.original_addEventListener = EventTarget.prototype.addEventListener;

    function addEventListener_hook(typ, fn, opt) {
        console.log('--- add event listener',this.nodeName,typ);
        this.all_handlers = this.all_handlers || [];
        this.all_handlers.push({typ,fn,opt});
        this.original_addEventListener(typ, fn, opt);  
    }

    EventTarget.prototype.addEventListener = addEventListener_hook;
}

คุณควรแทรกโค้ดนี้ไว้ใกล้ด้านบนของหน้าเว็บหลักของคุณ (เช่น index.html ) ในระหว่างการล้างข้อมูลคุณสามารถวนลูปผ่าน all_handlers และเรียกใช้ removeEventHandler สำหรับแต่ละรายการ ไม่ต้องกังวลกับการเรียก removeEventHandler หลายครั้งด้วยฟังก์ชันเดียวกัน มันไม่เป็นอันตราย

ตัวอย่างเช่น,

function cleanup(elem) {
    for (let t in elem) if (t.startsWith('on') && elem[t] != null) {
        elem[t] = null;
        console.log('cleanup removed listener from '+elem.nodeName,t);
    } 
    for (let t of elem.all_handlers || []) {
        elem.removeEventListener(t.typ, t.fn, t.opt);
        console.log('cleanup removed listener from '+elem.nodeName,t.typ);
    } 
}

หมายเหตุ:สำหรับ IE ใช้ Element แทน EventTarget และเปลี่ยน => เป็นฟังก์ชันและอื่น ๆ อีกมากมาย


0

เพื่อให้คำตอบสมบูรณ์ต่อไปนี้เป็นตัวอย่างการลบเหตุการณ์ในโลกแห่งความเป็นจริงเมื่อคุณเข้าชมเว็บไซต์และไม่มีการควบคุมโค้ด HTML และ JavaScript ที่สร้างขึ้น

เว็บไซต์ที่น่ารำคาญบางแห่งป้องกันไม่ให้คุณคัดลอกและวางชื่อผู้ใช้ในแบบฟอร์มการเข้าสู่ระบบซึ่งสามารถข้ามได้อย่างง่ายดายหากมีการonpasteเพิ่มเหตุการณ์ด้วยonpaste="return false"แอตทริบิวต์ HTML ในกรณีนี้เราต้องคลิกขวาที่ช่องป้อนข้อมูลเลือก "ตรวจสอบองค์ประกอบ" ในเบราว์เซอร์เช่น Firefox และลบแอตทริบิวต์ HTML

อย่างไรก็ตามหากมีการเพิ่มเหตุการณ์ผ่าน JavaScript เช่นนี้:

document.getElementById("lyca_login_mobile_no").onpaste = function(){return false};

เราจะต้องลบเหตุการณ์ผ่าน JavaScript ด้วย:

document.getElementById("lyca_login_mobile_no").onpaste = null;

ในตัวอย่างของฉันฉันใช้ ID "lyca_login_mobile_no" เนื่องจากเป็นรหัสป้อนข้อความที่ใช้โดยเว็บไซต์ที่ฉันเข้าชม

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

นอกจากนี้ยังสามารถทำได้เร็วขึ้นและทำงานอัตโนมัติผ่าน JavaScript:

var oldNode = document.getElementById("lyca_login_mobile_no");
var newNode = oldNode.cloneNode(true);
oldNode.parentNode.insertBefore(newNode, oldNode);
oldNode.parentNode.removeChild(oldNode);

อัปเดต: หากเว็บแอปสร้างขึ้นโดยใช้เฟรมเวิร์ก JavaScript เช่น Angular ดูเหมือนว่าโซลูชันก่อนหน้านี้ไม่ทำงานหรือทำให้แอปเสียหาย วิธีแก้ปัญหาอื่นในการอนุญาตการวางคือการตั้งค่าผ่าน JavaScript:

document.getElementById("lyca_login_mobile_no").value = "username";

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


0

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

const REMOVE_ALL_LISTENERS_EVENT_LISTENER = 'removeAllListeners';

    proto[REMOVE_ALL_LISTENERS_EVENT_LISTENER] = function () {
        const target = this || _global;
        const eventName = arguments[0];
        if (!eventName) {
            const keys = Object.keys(target);
            for (let i = 0; i < keys.length; i++) {
                const prop = keys[i];
                const match = EVENT_NAME_SYMBOL_REGX.exec(prop);
                let evtName = match && match[1];
                // in nodejs EventEmitter, removeListener event is
                // used for monitoring the removeListener call,
                // so just keep removeListener eventListener until
                // all other eventListeners are removed
                if (evtName && evtName !== 'removeListener') {
                    this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, evtName);
                }
            }
            // remove removeListener listener finally
            this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, 'removeListener');
        }
        else {
            const symbolEventNames = zoneSymbolEventNames$1[eventName];
            if (symbolEventNames) {
                const symbolEventName = symbolEventNames[FALSE_STR];
                const symbolCaptureEventName = symbolEventNames[TRUE_STR];
                const tasks = target[symbolEventName];
                const captureTasks = target[symbolCaptureEventName];
                if (tasks) {
                    const removeTasks = tasks.slice();
                    for (let i = 0; i < removeTasks.length; i++) {
                        const task = removeTasks[i];
                        let delegate = task.originalDelegate ? task.originalDelegate : task.callback;
                        this[REMOVE_EVENT_LISTENER].call(this, eventName, delegate, task.options);
                    }
                }
                if (captureTasks) {
                    const removeTasks = captureTasks.slice();
                    for (let i = 0; i < removeTasks.length; i++) {
                        const task = removeTasks[i];
                        let delegate = task.originalDelegate ? task.originalDelegate : task.callback;
                        this[REMOVE_EVENT_LISTENER].call(this, eventName, delegate, task.options);
                    }
                }
            }
        }
        if (returnTarget) {
            return this;
        }
    };

....


อยู่ที่ไหนEVENT_NAME_SYMBOL_REGXควรจะมาจากไหน?
Elias Zamaria

0

คุณสามารถเพิ่มฟังก์ชันตัวช่วยที่ล้างตัวฟังเหตุการณ์ได้เช่น

function clearEventListener(element) {
 const clonedElement = element.cloneNode(true);
element.replaceWith(clonedElement);
return clonedElement;
}

เพียงแค่ส่งผ่านองค์ประกอบไปยังฟังก์ชันและนั่นก็คือ ...


-1
var div = getElementsByTagName('div')[0]; /* first div found; you can use getElementById for more specific element */
div.onclick = null; // OR:
div.onclick = function(){};

// แก้ไข

ฉันไม่รู้ว่าคุณใช้วิธีใดในการแนบเหตุการณ์ สำหรับaddEventListenerคุณสามารถใช้สิ่งนี้:

div.removeEventListener('click',functionName,false); // functionName is the name of your callback function

เพิ่มเติม รายละเอียด


1
ถ้าฉันตั้งค่าเป็นเช่นdiv.onClick = somethingนั้นมันจะตั้งค่าเหตุการณ์อื่นในคลิก แต่การเข้าพักก่อนหน้านี้ดังนั้นฉันจึงมีอยู่ก่อนหน้านี้และอีกอย่างหนึ่งเช่น null ...
Florian Müller

1
addEventListener()จะไม่ทำงานหากมีการเพิ่มรถขนผ่าน
Felix Kling

@ ฟลอริดา: ไม่นั่นไม่เป็นความจริง หากคุณใช้วิธีนี้ในการเพิ่มตัวจัดการเหตุการณ์คุณจะมีตัวจัดการเหตุการณ์ได้เพียงคนเดียว แต่จะสร้างความแตกต่างได้ถ้าคุณใช้addEventListener()...
Felix Kling

แต่ฉันเคยเห็นแบบนี้แล้วดังนั้นถ้าฉันเพิ่มฟังก์ชันก่อนหน้านี้อีกครั้งมันจะถูกเรียกสองครั้ง ... และอย่างที่เฟลิกซ์คิงบอกว่าสิ่งนี้ใช้ไม่ได้กับ addEventListener ...
Florian Müller

-1

อาจเป็นเพราะเบราว์เซอร์จะทำเพื่อคุณหากคุณทำสิ่งต่อไปนี้

คัดลอกdivและแอตทริบิวต์และแทรกไว้ก่อนหน้าเก่าจากนั้นย้ายเนื้อหาจากเก่าไปใหม่และลบเก่า


ตอนนี้เป็นความคิดที่ดี แต่ไม่มีประสิทธิภาพโดยสิ้นเชิงถ้าคุณต้องทำสิ่งนี้เพื่อให้ได้ div ระหว่าง 1'000 ถึง 1'000'000 ... ใช่มันเป็นโครงการใหญ่;)
Florian Müller

6
1M divs ในหนึ่งหน้า? คุณจะประสบปัญหาอื่นก่อนหน้านี้) อาจจะใช้การมอบหมายเหตุการณ์แล้ว ...
ไมค์

-1

การลบเหตุการณ์ทั้งหมดในdocument :

หนึ่งในสายการบิน:

for (key in getEventListeners(document)) { getEventListeners(document)[key].forEach(function(c) { c.remove() }) }

เวอร์ชั่นสวย:

for (key in getEventListeners(document)) {
  getEventListeners(document)[key].forEach(function(c) {
    c.remove()
  })   
}

-1

Chrome เท่านั้น

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

function(eventType){
    getEventListeners(window).[eventType].forEach(
        function(e){
            window.removeEventListener(eventType, e.listener)
        }
    );
}

ฉันหวังว่านี่จะช่วยใครบางคนได้เนื่องจากคำตอบก่อนหน้านี้ใช้วิธี "ลบ" ซึ่งตั้งแต่นั้นมาก็ใช้ไม่ได้อีกต่อไป


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