วิธีตรวจสอบว่ามีตัวฟังเหตุการณ์ที่แนบแบบไดนามิกอยู่หรือไม่?


113

นี่คือปัญหาของฉัน: เป็นไปได้ไหมที่จะตรวจสอบว่ามีตัวฟังเหตุการณ์ที่แนบแบบไดนามิกอยู่หรือไม่? หรือฉันจะตรวจสอบสถานะของคุณสมบัติ "onclick" (?) ใน DOM ได้อย่างไร ฉันค้นหาอินเทอร์เน็ตเช่นเดียวกับ Stack Overflow เพื่อหาวิธีแก้ปัญหา แต่ไม่มีโชค นี่คือ html ของฉัน:

<a id="link1" onclick="linkclick(event)"> link 1 </a>
<a id="link2"> link 2 </a> <!-- without inline onclick handler -->

จากนั้นใน Javascript ฉันแนบตัวฟังเหตุการณ์ที่สร้างขึ้นแบบไดนามิกไปยังลิงก์ที่ 2:

document.getElementById('link2').addEventListener('click', linkclick, false);

รหัสทำงานได้ดี แต่ความพยายามทั้งหมดของฉันในการตรวจจับตัวฟังที่แนบมานั้นล้มเหลว:

// test for #link2 - dynamically created eventlistener
alert(elem.onclick); // null
alert(elem.hasAttribute('onclick')); // false
alert(elem.click); // function click(){[native code]} // btw, what's this?

jsFiddle อยู่ที่นี่ หากคุณคลิก "Add onclick for 2" แล้วคลิก "[link 2]" เหตุการณ์จะเริ่มทำงานได้ดี แต่ "Test link 2" จะรายงานเท็จเสมอ ใครสามารถช่วย?


1
ฉันขอโทษที่ต้องพูด แต่เป็นไปไม่ได้ที่จะรับการเชื่อมโยงเหตุการณ์โดยใช้วิธีการปัจจุบันของคุณ: stackoverflow.com/questions/5296858/…
Ivan

1
คุณสามารถทำได้โดยใช้เครื่องมือ chrome dev: stackoverflow.com/a/41137585/863115
arufian

คุณสามารถทำได้โดยสร้างที่เก็บข้อมูลของคุณเองเพื่อติดตามผู้ฟัง ดูคำตอบของฉันสำหรับข้อมูลเพิ่มเติม
Angel Politis

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

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

คำตอบ:


50

ไม่มีวิธีตรวจสอบว่ามีผู้ฟังเหตุการณ์ที่แนบแบบไดนามิกอยู่หรือไม่

วิธีเดียวที่คุณจะดูได้ว่ามีการแนบผู้ฟังเหตุการณ์หรือไม่คือการแนบตัวฟังเหตุการณ์เช่นนี้:

elem.onclick = function () { console.log (1) }

จากนั้นคุณสามารถทดสอบว่ามีการแนบ Listener เหตุการณ์หรือไม่onclickโดยการส่งคืน!!elem.onclick(หรือสิ่งที่คล้ายกัน)


45

ฉันทำอะไรแบบนั้น:

const element = document.getElementById('div');

if (element.getAttribute('listener') !== 'true') {
     element.addEventListener('click', function (e) {
         const elementClicked = e.target;
         elementClicked.setAttribute('listener', 'true');
         console.log('event has been attached');
    });
}

การสร้างแอตทริบิวต์พิเศษสำหรับองค์ประกอบเมื่อแนบ Listener แล้วตรวจสอบว่ามีอยู่หรือไม่


5
นี่คือแนวทางที่ฉันเคยใช้ในอดีต คำแนะนำของฉันคือการใช้โครงสร้างไวยากรณ์ที่เฉพาะเจาะจงมาก IE แทนที่จะเป็น "ผู้ฟัง" ให้ใช้เหตุการณ์นั้นเอง ดังนั้น "data-event-click" สิ่งนี้ให้ความยืดหยุ่นในกรณีที่คุณต้องการทำมากกว่าหนึ่งเหตุการณ์และช่วยให้อ่านง่ายขึ้น
conrad10781

สิ่งนี้ด้วยการเพิ่มของ @ conrad10781 ดูเหมือนจะเป็นวิธีที่น่าเชื่อถือและง่ายที่สุด หากมีการแสดงผลองค์ประกอบด้วยเหตุผลใดก็ตามและตัวฟังเหตุการณ์ถูกตัดการเชื่อมต่อแอตทริบิวต์นี้จะถูกรีเซ็ตด้วย
Arye Eidelman

25

สิ่งที่ฉันจะทำคือสร้างบูลีนนอกฟังก์ชันของคุณที่เริ่มต้นเป็น FALSE และตั้งค่าเป็น TRUE เมื่อคุณแนบเหตุการณ์ นี่จะเป็นธงบางประเภทสำหรับคุณก่อนที่คุณจะแนบกิจกรรมอีกครั้ง นี่คือตัวอย่างของแนวคิด

// initial load
var attached = false;

// this will only execute code once
doSomething = function()
{
 if (!attached)
 {
  attached = true;
  //code
 }
} 

//attach your function with change event
window.onload = function()
{
 var txtbox = document.getElementById('textboxID');

 if (window.addEventListener)
 {
  txtbox.addEventListener('change', doSomething, false);
 }
 else if(window.attachEvent)
 {
  txtbox.attachEvent('onchange', doSomething);
 }
}

นี้คุณจะต้องเปลี่ยนรหัสของเหตุการณ์ที่แนบมาเพื่อติดตามว่ามีการแนบ (คล้ายกับนี้หรือคำตอบนี้ ) จะไม่ทำงานหากคุณไม่ได้ควบคุมโค้ด
user202729

8

อาจซ้ำกันได้: ตรวจสอบว่าองค์ประกอบมีตัวฟังเหตุการณ์อยู่หรือไม่ ไม่มี jQuery โปรดหาคำตอบของฉันที่นั่น

โดยทั่วไปนี่คือเคล็ดลับสำหรับเบราว์เซอร์ Chromium (Chrome):

getEventListeners(document.querySelector('your-element-selector'));

1
แม่นยำมากขึ้นที่นี่: stackoverflow.com/a/41137585/1431728
JohnK

7

tl; dr : ไม่คุณไม่สามารถทำได้ด้วยวิธีใด ๆ ที่ได้รับการสนับสนุน


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

/* Create a storage object. */
var CustomEventStorage = [];

ขั้นตอนที่ 1:ขั้นแรกคุณจะต้องมีฟังก์ชันที่สามารถสำรวจวัตถุจัดเก็บข้อมูลและส่งคืนบันทึกขององค์ประกอบที่กำหนดองค์ประกอบ (หรือเท็จ)

/* The function that finds a record in the storage by a given element. */
function findRecordByElement (element) {
    /* Iterate over every entry in the storage object. */
    for (var index = 0, length = CustomEventStorage.length; index < length; index++) {
        /* Cache the record. */
        var record = CustomEventStorage[index];

        /* Check whether the given element exists. */
        if (element == record.element) {
            /* Return the record. */
            return record;
        }
    }

    /* Return false by default. */
    return false;
}

ขั้นตอนที่ 2:จากนั้นคุณจะต้องมีฟังก์ชันที่สามารถเพิ่มตัวฟังเหตุการณ์ แต่ยังแทรกตัวฟังลงในวัตถุจัดเก็บ

/* The function that adds an event listener, while storing it in the storage object. */
function insertListener (element, event, listener, options) {
    /* Use the element given to retrieve the record. */
    var record = findRecordByElement(element);

    /* Check whether any record was found. */
    if (record) {
        /* Normalise the event of the listeners object, in case it doesn't exist. */
        record.listeners[event] = record.listeners[event] || [];
    }
    else {
        /* Create an object to insert into the storage object. */
        record = {
            element: element,
            listeners: {}
        };

        /* Create an array for event in the record. */
        record.listeners[event] = [];

        /* Insert the record in the storage. */
        CustomEventStorage.push(record);
    }

    /* Insert the listener to the event array. */
    record.listeners[event].push(listener);

    /* Add the event listener to the element. */
    element.addEventListener(event, listener, options);
}

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

/* The function that checks whether an event listener is set for a given event. */
function listenerExists (element, event, listener) {
    /* Use the element given to retrieve the record. */
    var record = findRecordByElement(element);

    /* Check whether a record was found & if an event array exists for the given event. */
    if (record && event in record.listeners) {
        /* Return whether the given listener exists. */
        return !!~record.listeners[event].indexOf(listener);
    }

    /* Return false by default. */
    return false;
}

ขั้นตอนที่ 4:สุดท้ายคุณจะต้องมีฟังก์ชันที่สามารถลบผู้ฟังออกจากวัตถุจัดเก็บ

/* The function that removes a listener from a given element & its storage record. */
function removeListener (element, event, listener, options) {
    /* Use the element given to retrieve the record. */
    var record = findRecordByElement(element);

    /* Check whether any record was found and, if found, whether the event exists. */
    if (record && event in record.listeners) {
        /* Cache the index of the listener inside the event array. */
        var index = record.listeners[event].indexOf(listener);

        /* Check whether listener is not -1. */
        if (~index) {
            /* Delete the listener from the event array. */
            record.listeners[event].splice(index, 1);
        }

        /* Check whether the event array is empty or not. */
        if (!record.listeners[event].length) {
            /* Delete the event array. */
            delete record.listeners[event];
        }
    }

    /* Add the event listener to the element. */
    element.removeEventListener(event, listener, options);
}

ตัวอย่างข้อมูล:


แม้ว่า OP จะโพสต์คำถามไปแล้วกว่า 5 ปี แต่ฉันเชื่อว่าคนที่สะดุดกับคำถามนี้ในอนาคตจะได้รับประโยชน์จากคำตอบนี้ดังนั้นอย่าลังเลที่จะให้ข้อเสนอแนะหรือการปรับปรุง 😊


ขอบคุณสำหรับวิธีแก้ปัญหานี้ตรงไปตรงมาและทนทาน ข้อสังเกตประการหนึ่งมีข้อผิดพลาดเล็กน้อย ฟังก์ชัน "removeListener" ตรวจสอบว่าอาร์เรย์เหตุการณ์ว่างเปล่าหรือไม่ แต่ส่วนท้ายไม่ถูกต้อง ควรพูดว่า "if (record.listeners [event] .length! = - 1)"
JPA

5

คุณสามารถตรวจสอบด้วยตนเองได้ตลอดเวลาว่ามี EventListener หรือไม่โดยใช้ตัวตรวจสอบ Chrome เป็นต้น ในแท็บองค์ประกอบคุณมีแท็บย่อย "สไตล์" แบบดั้งเดิมและอยู่ใกล้อีกแท็บหนึ่ง: "Listeners เหตุการณ์" ซึ่งจะให้รายชื่อ EventListeners ทั้งหมดที่มีองค์ประกอบที่เชื่อมโยง


5

ดูเหมือนแปลกที่ไม่มีวิธีนี้ ถึงเวลาที่จะเพิ่มในที่สุด?

หากคุณต้องการคุณสามารถทำสิ่งต่อไปนี้:

var _addEventListener = EventTarget.prototype.addEventListener;
var _removeEventListener = EventTarget.prototype.removeEventListener;
EventTarget.prototype.events = {};
EventTarget.prototype.addEventListener = function(name, listener, etc) {
  var events = EventTarget.prototype.events;
  if (events[name] == null) {
    events[name] = [];
  }

  if (events[name].indexOf(listener) == -1) {
    events[name].push(listener);
  }

  _addEventListener(name, listener);
};
EventTarget.prototype.removeEventListener = function(name, listener) {
  var events = EventTarget.prototype.events;

  if (events[name] != null && events[name].indexOf(listener) != -1) {
    events[name].splice(events[name].indexOf(listener), 1);
  }

  _removeEventListener(name, listener);
};
EventTarget.prototype.hasEventListener = function(name) {
  var events = EventTarget.prototype.events;
  if (events[name] == null) {
    return false;
  }

  return events[name].length;
};

3

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

var eventHandlerType;

$('#contentDiv').on('click', clickEventHandler).triggerHandler('click');

function clickEventHandler(e) {
    eventHandlerType = e.type;
}

if (eventHandlerType === 'click') {
    console.log('EventHandler "click" has been applied');
}

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

2

ดูเหมือนจะไม่มีฟังก์ชันเบราว์เซอร์ข้ามที่ค้นหาเหตุการณ์ที่ลงทะเบียนภายใต้องค์ประกอบที่กำหนด

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

Firefox

ขั้นแรกให้ดูองค์ประกอบในแท็บตัวตรวจสอบภายในเครื่องมือสำหรับนักพัฒนา สิ่งนี้สามารถทำได้:

  • ในหน้าโดยคลิกขวาที่รายการบนเว็บเพจที่คุณต้องการตรวจสอบและเลือก "ตรวจสอบองค์ประกอบ" จากเมนู
  • ภายในคอนโซลโดยใช้ฟังก์ชันเพื่อเลือกองค์ประกอบเช่นdocument.querySelectorจากนั้นคลิกไอคอนข้างองค์ประกอบเพื่อดูในแท็บตัวตรวจสอบ

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

โครเมียม

ขั้นแรกให้ดูองค์ประกอบในแท็บองค์ประกอบภายในเครื่องมือสำหรับนักพัฒนา สิ่งนี้สามารถทำได้:

  • ในหน้าโดยคลิกขวาที่รายการบนเว็บเพจที่คุณต้องการตรวจสอบและเลือก "ตรวจสอบ" จากเมนู
  • ภายในคอนโซลโดยใช้ฟังก์ชันเพื่อเลือกองค์ประกอบเช่นdocument.querySelectorคลิกขวาที่องค์ประกอบแล้วเลือก "เปิดเผยในแผงองค์ประกอบ" เพื่อดูในแท็บตัวตรวจสอบ

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

ใน Chrome เหตุการณ์สำหรับองค์ประกอบสามารถพบได้โดยใช้ฟังก์ชันgetEventListeners อย่างไรก็ตามจากการทดสอบของฉันฟังก์ชันgetEventListenersจะไม่แสดงรายการเหตุการณ์เมื่อมีการส่งผ่านองค์ประกอบหลายรายการ หากคุณต้องการค้นหาองค์ประกอบทั้งหมดในเพจที่มีผู้ฟังและดูฟังก์ชันเรียกกลับสำหรับผู้ฟังเหล่านั้นคุณสามารถใช้รหัสต่อไปนี้ในคอนโซลเพื่อดำเนินการนี้:

var elems = document.querySelectorAll('*');

for (var i=0; i <= elems.length; i++) {
    var listeners = getEventListeners(elems[i]);

    if (Object.keys(listeners).length < 1) {
        continue;
    }

    console.log(elems[i]);

    for (var j in listeners) {
        console.log('Event: '+j);

        for (var k=0; k < listeners[j].length; k++) {
            console.log(listeners[j][k].listener);
        }
    }
}

โปรดแก้ไขคำตอบนี้หากคุณรู้วิธีดำเนินการในเบราว์เซอร์ที่กำหนดหรือในเบราว์เซอร์อื่น


1

ฉันเพิ่งค้นพบสิ่งนี้โดยพยายามดูว่ากิจกรรมของฉันติดอยู่หรือไม่ ....

ถ้าคุณทำ :

item.onclick

มันจะคืนค่า "null"

แต่ถ้าคุณทำ:

item.hasOwnProperty('onclick')

จากนั้นจึงเป็น "TRUE"

ดังนั้นฉันคิดว่าเมื่อคุณใช้ "addEventListener" เพื่อเพิ่มตัวจัดการเหตุการณ์วิธีเดียวที่จะเข้าถึงได้คือผ่าน "hasOwnProperty" ฉันหวังว่าฉันจะรู้ว่าทำไมหรืออย่างไร แต่อนิจจาหลังจากค้นคว้าแล้วฉันยังไม่พบคำอธิบาย


2
onclickแยกจาก.addEventListener- มีผลต่อแอตทริบิวต์ในขณะที่.addEventListenerไม่มี
Zach Saucier

1

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

ดังนั้นโค้ดเฉพาะกิจบางอย่างจะเติมเต็มช่องว่างเพื่อจัดการขั้นตอนการเขียนโค้ดของคุณ วิธีปฏิบัติคือการสร้างstateตัวแปรโดยใช้ ตัวอย่างเช่นแนบตัวตรวจสอบผู้ฟังดังต่อไปนี้:

var listenerPresent=false

จากนั้นหากคุณตั้งค่าผู้ฟังเพียงแค่เปลี่ยนค่า:

listenerPresent=true

จากนั้นภายในการเรียกกลับของ eventListener ของคุณคุณสามารถกำหนดฟังก์ชันเฉพาะภายในและในลักษณะเดียวกันนี้แจกจ่ายการเข้าถึงฟังก์ชันการทำงานโดยขึ้นอยู่กับสถานะบางอย่างเป็นตัวแปรเช่น:

accessFirstFunctionality=false
accessSecondFunctionality=true
accessThirdFunctionality=true

1

เพียงลบเหตุการณ์ก่อนเพิ่ม:

document.getElementById('link2').removeEventListener('click', linkclick, false);
document.getElementById('link2').addEventListener('click', linkclick, false);

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

0

ฉันเพิ่งเขียนสคริปต์ที่ช่วยให้คุณบรรลุเป้าหมายนี้ มันมีฟังก์ชั่นระดับโลกสองฟังก์ชัน: hasEvent(Node elm, String event)และgetEvents(Node elm)คุณสามารถใช้ประโยชน์ได้ โปรดทราบว่ามันปรับเปลี่ยนEventTargetวิธีการต้นแบบadd/RemoveEventListenerและไม่ทำงานสำหรับเหตุการณ์ที่เพิ่มผ่านมาร์กอัป HTML หรือไวยากรณ์จาวาสคริปต์ของelm.on_event = ...

ข้อมูลเพิ่มเติมที่ GitHub

การสาธิตสด

สคริปต์:

var hasEvent,getEvents;!function(){function b(a,b,c){c?a.dataset.events+=","+b:a.dataset.events=a.dataset.events.replace(new RegExp(b),"")}function c(a,c){var d=EventTarget.prototype[a+"EventListener"];return function(a,e,f,g,h){this.dataset.events||(this.dataset.events="");var i=hasEvent(this,a);return c&&i||!c&&!i?(h&&h(),!1):(d.call(this,a,e,f),b(this,a,c),g&&g(),!0)}}hasEvent=function(a,b){var c=a.dataset.events;return c?new RegExp(b).test(c):!1},getEvents=function(a){return a.dataset.events.replace(/(^,+)|(,+$)/g,"").split(",").filter(function(a){return""!==a})},EventTarget.prototype.addEventListener=c("add",!0),EventTarget.prototype.removeEventListener=c("remove",!1)}();

-1

ในทางทฤษฎี piggyback addEventListener และ removeEventListener เพื่อเพิ่มลบแฟล็กให้กับวัตถุ 'this' น่าเกลียดและฉันยังไม่ได้ทดสอบ ...

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