Dinamik olarak eklenmiş olay dinleyicisinin var olup olmadığı nasıl kontrol edilir?


113

İşte benim sorunum: dinamik olarak eklenmiş olay dinleyicisinin varlığını bir şekilde kontrol etmek mümkün mü? Veya DOM'daki "onclick" (?) Özelliğinin durumunu nasıl kontrol edebilirim? Bir çözüm için Stack Overflow gibi interneti aradım ama şansım yok. İşte benim html'im:

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

Sonra Javascript'te dinamik olarak oluşturulmuş olay dinleyicisini 2. bağlantıya ekliyorum:

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

Kod iyi çalışıyor, ancak ekli dinleyiciyi algılama girişimlerim başarısız oluyor:

// 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 burada . "2 için onclick ekle" ve ardından "[bağlantı 2]" yi tıklarsanız, olay iyi tetiklenir, ancak "Bağlantı 2'yi test et" her zaman yanlış bildirir. Biri yardım edebilir mi?


1
Söylediğim için üzgünüm, ancak geçerli yönteminizi kullanarak olay bağlamalarını almak imkansız: stackoverflow.com/questions/5296858/…
Ivan

1
chrome geliştirme aracını kullanarak bunu yapabilirsiniz: stackoverflow.com/a/41137585/863115
arufian

Dinleyicileri takip eden kendi depolama alanınızı oluşturarak bunu yapabilirsiniz. Daha fazlası için cevabıma bakın .
Melek Politis

Amaç, önceden eklenmiş bir olayın tekrar eklenmesini önlemekse, doğru cevap buradadır . Temel olarak, anonim bir işlev yerine adlandırılmış bir işlev kullanırsanız, yinelenen aynı olay dinleyicileri atılır ve bunlar için endişelenmenize gerek kalmaz.
Syknapse

Yinelenen dinleyicilere sahip olma konusunda endişeleriniz varsa, tekrar eklemeden önce geçerli olay dinleyicisini kaldırın. Mükemmel değil ama basit.
Jacksonkr

Yanıtlar:


49

Dinamik olarak eklenmiş olay dinleyicilerinin var olup olmadığını kontrol etmenin bir yolu yoktur.

Bir olay dinleyicisinin eklenip eklenmediğini görmenin tek yolu, aşağıdaki gibi olay dinleyicileri eklemektir:

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

Daha sonra onclickgeri dönerek !!elem.onclick(veya benzer bir şey) bir olay dinleyicisinin eklenip eklenmediğini test edebilirsiniz .


41

Ben böyle bir şey yaptım:

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

Dinleyici eklendiğinde bir öğe için özel nitelik oluşturma ve ardından var olup olmadığını kontrol etme.


5
Geçmişte kullandığım yaklaşım budur. Benim tavsiyem çok özel bir sözdizimi yapısı kullanmak olacaktır. IE, "dinleyici" yerine olayın kendisini kullanın. Yani "veri-olay-tıklama". Bu, birden fazla etkinlik yapmayı düşündüğünüz olayda biraz esneklik sağlar ve işleri biraz daha okunaklı tutar.
conrad10781

@ Conrad10781'in eklenmesiyle bu, en güvenilir ve en basit yol gibi görünüyor. Herhangi bir nedenle öğe yeniden oluşturulmuşsa ve olay dinleyicinin bağlantısı kesilirse, bu öznitelik de sıfırlanacaktır.
Arye Eidelman

25

Yapacağım şey, işlevinizin dışında FALSE olarak başlayan ve olayı eklediğinizde TRUE olarak ayarlanan bir Boole oluşturmaktır. Bu, etkinliği tekrar eklemeden önce sizin için bir tür bayrak görevi görür. İşte fikrin bir örneği.

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

Bu ekli olup olmadığını takip (benzer tutmaya bağlı olayın kodunu değiştirmek gerektirir bu ya bu cevap ). Kodu kontrol etmezseniz işe yaramaz.
user202729


7

tl; dr : Hayır, bunu yerel olarak desteklenen herhangi bir şekilde yapamazsınız.


Bunu başarmanın tek yolu, eklenen dinleyicilerin bir kaydını tuttuğunuz özel bir depolama nesnesi oluşturmaktır. Aşağıdaki satırlarda bir şey:

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

Adım 1: Öncelikle, depolama nesnesini geçebilecek ve öğeye (veya yanlış) verilen bir öğenin kaydını döndürebilecek bir işleve ihtiyacınız olacak.

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

Adım 2: Daha sonra, bir olay dinleyicisi ekleyebilen ancak aynı zamanda dinleyiciyi depolama nesnesine ekleyebilen bir işleve ihtiyacınız olacaktır.

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

Adım 3: Sorunuzun gerçek gereksinimi ile ilgili olarak, bir öğenin belirli bir olay için bir olay dinleyicisi eklenip eklenmediğini kontrol etmek için aşağıdaki işleve ihtiyacınız olacaktır.

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

Adım 4: Son olarak, bir dinleyiciyi depolama nesnesinden silebilecek bir işleve ihtiyacınız olacak.

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

Snippet:


OP'nin soruyu yayınlamasının üzerinden 5 yıldan fazla zaman geçmiş olmasına rağmen, gelecekte bu soruyla karşılaşan kişilerin bu yanıttan yararlanacağına inanıyorum, bu nedenle ona önerilerde bulunmaktan veya iyileştirmeler yapmaktan çekinmeyin. 😊


Bu çözüm için teşekkürler, basit ve sağlam. Yine de bir açıklama, küçük bir hata var. "RemoveListener" işlevi, olay dizisinin boş olup olmadığını kontrol eder, ancak üçlü yanlıştır. "İf (record.listeners [olay] .length! = - 1)" demelidir.
JPA

5

Örneğin, Chrome denetçisini kullanarak EventListener'ınızın mevcut olup olmadığını her zaman manuel olarak kontrol edebilirsiniz. Öğe sekmesinde, geleneksel "Stiller" alt sekmesine sahipsiniz ve ona yakın bir yerde: "Etkinlik Dinleyicileri". Size tüm EventListener'ların listesini, bağlantılı öğeleriyle birlikte verecektir.


5

Bu yöntemin olmaması garip görünüyor. Sonunda eklemenin zamanı geldi mi?

İsterseniz aşağıdakine benzer bir şey yapabilirsiniz:

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

İşte dinamik olarak eklenmiş bir olay dinleyicisinin varlığını kontrol etmek için kullandığım bir komut dosyası. Bir öğeye bir olay işleyicisi eklemek için jQuery kullandım, ardından bu olayı tetikledim (bu durumda 'tıklama' olayı). Bu şekilde, yalnızca olay işleyicisi bağlıysa var olacak olay özelliklerini alabilir ve yakalayabilirim.

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
Ayrıca, öğenize, temelde kendisine eklenmiş bir olayı olan öğe olarak işaretleyen bir özellik atayabilirsiniz;
Justin

2

Belirli bir öğe altında kaydedilen olayları arayan bir çapraz tarayıcı işlevi görünmüyor.

Ancak, geliştirme araçlarını kullanarak bazı tarayıcılardaki öğeler için geri arama işlevlerini görüntülemek mümkündür. Bu, bir web sayfasının nasıl çalıştığını belirlemeye çalışırken veya kodda hata ayıklamak için yararlı olabilir.

Firefox

İlk olarak, geliştirici araçları içindeki Denetçi sekmesindeki öğeyi görüntüleyin . Bu yapılabilir:

  • Sayfada incelemek istediğiniz öğeye sağ tıklayıp menüden "Öğeyi İncele" yi seçin.
  • Konsolda , document.querySelector gibi bir öğeyi seçmek için bir işlev kullanarak ve ardından öğeyi Denetçi sekmesinde görüntülemek için öğenin yanındaki simgeye tıklayarak .

Öğeye herhangi bir olay kaydedildiyse, öğenin yanında Olay sözcüğünü içeren bir düğme göreceksiniz . Buna tıklamak, öğeyle kaydedilmiş olayları görmenizi sağlayacaktır. Bir olayın yanındaki oku tıklamak, onun için geri arama işlevini görüntülemenizi sağlar.

Krom

Öncelikle, geliştirici araçlarının içindeki Öğeler sekmesindeki öğeyi görüntüleyin . Bu yapılabilir:

  • Sayfada incelemek istediğiniz öğeye sağ tıklayarak ve menüden "İncele" yi seçerek
  • Konsolda , document.querySelector gibi bir öğeyi seçmek için bir işlev kullanarak, öğeye sağ tıklayın ve Denetçi sekmesinde görüntülemek için "Öğelerde Göster" öğesini seçin .

Web sayfası öğelerini içeren ağacı gösteren pencerenin bölümünün yakınında, "Olay Dinleyicileri" başlıklı bir sekme olan başka bir bölüm bulunmalıdır. Öğeye kayıtlı olayları görmek için seçin. Belirli bir etkinliğin kodunu görmek için, sağındaki bağlantıyı tıklayın.

Chrome'da, bir öğeye ilişkin etkinlikler getEventListeners işlevi kullanılarak da bulunabilir . Bununla birlikte, testlerime göre, getEventListeners işlevi, kendisine birden çok öğe iletildiğinde olayları listelemiyor. Sayfada dinleyicileri olan tüm öğeleri bulmak ve bu dinleyiciler için geri arama işlevlerini görüntülemek istiyorsanız, bunu yapmak için konsolda aşağıdaki kodu kullanabilirsiniz:

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

Belirtilen tarayıcılarda veya diğer tarayıcılarda bunu yapmanın yollarını biliyorsanız, lütfen bu yanıtı düzenleyin.


1

Bunu sadece etkinliğimin eklenip eklenmediğini görmeye çalışarak buldum ...

yaparsan :

item.onclick

"boş" döndürür

ama yaparsan:

item.hasOwnProperty('onclick')

o zaman "DOĞRU"

bu yüzden olay işleyicileri eklemek için "addEventListener" kullandığınızda, ona erişmenin tek yolunun "hasOwnProperty" olduğunu düşünüyorum. Keşke neden veya nasıl olduğunu bilseydim ama ne yazık ki araştırdıktan sonra bir açıklama bulamadım.


2
onclickayrıdır .addEventListenerederken bir öznitelik etkiler - .addEventListenerdeğil
Zach Saucier

1

İyi anlarsam, yalnızca bir dinleyicinin kontrol edilip edilmediğini kontrol edebilirsiniz, ancak hangi dinleyicinin sunum yapan kişi olduğunu bilemezsiniz.

Bu nedenle, bazı özel kodlar, kodlama akışınızı idare etmek için boşluğu doldurur. Pratik bir yöntem, statedeğişkenleri kullanarak oluşturmaktır . Örneğin, aşağıdaki gibi bir dinleyicinin denetleyicisini ekleyin:

var listenerPresent=false

o zaman bir dinleyici ayarlarsanız, yalnızca değeri değiştirin:

listenerPresent=true

daha sonra eventListener'ınızın geri çağrısının içinde, içinde belirli işlevler atayabilir ve bu şekilde, bazı duruma bağlı olarak işlevlere erişimi değişken olarak dağıtabilirsiniz, örneğin:

accessFirstFunctionality=false
accessSecondFunctionality=true
accessThirdFunctionality=true

1

Etkinliği eklemeden önce kaldırmanız yeterlidir:

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

Bu iyi bir numara, ancak bunun yalnızca şu anda "eklenen" olay dinleyicilerinden biri olan işleve bir ref varsa çalıştığını varsayıyorum. Bunun standart API'nin bir eksikliği olduğunu düşünüyorum, eğer herhangi bir olay dinleyicisi ekleyebilirsek, şimdiye kadar hangi olay dinleyicilerin eklendiğini kontrol etmek de mümkün olmalı. Neredeyse olay dinleyicileri şu anda belirsiz bir kavram gibi görünen "yalnızca yazılan değişkenler" gibidir.
Panu Logic

0

Bunu başarmanıza izin veren bir senaryo yazdım. Size iki genel işlev sunar: hasEvent(Node elm, String event)ve getEvents(Node elm)bunları kullanabileceğiniz. EventTargetPrototip yöntemini değiştirdiğini ve add/RemoveEventListenerHTML biçimlendirmesi veya javascript sözdizimi aracılığıyla eklenen olaylar için çalışmadığını unutmayın.elm.on_event = ...

GitHub'da daha fazla bilgi

Canlı Demo

Senaryo:

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

Teoride, 'this' nesnesine bir bayrak eklemek için addEventListener ve removeEventListener'ı bindirebilirsiniz. Çirkin ve test etmedim ...

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.