動的にアタッチされたイベントリスナーが存在するかどうかを確認するにはどうすればよいですか?


103

これが私の問題です:動的にアタッチされたイベントリスナーの存在を何らかの方法でチェックすることは可能ですか?または、DOMの「onclick」(?)プロパティのステータスを確認するにはどうすればよいですか?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はこちらです。「onclick for 2を追加」をクリックしてから「[リンク2]」をクリックすると、イベントは正常に起動しますが、「テストリンク2」は常にfalseを報告します。誰か助けてもらえますか?


1
私が言って申し訳ありませんが、あなたの現在のメソッドを使用してイベントバインディングを取得することは不可能である。stackoverflow.com/questions/5296858/...
イワン

1
chrome devツールを使用してそれを行うことができます:stackoverflow.com/a/41137585/863115
arufian

これは、リスナーを追跡する独自のストレージを作成することで実現できます。詳細については私の答えを参照しください。
エンジェルポリティス2017年

すでに追加されているイベントが再度追加されないようにすることが目的である場合、正しい答えはここにあります。基本的に、無名関数の代わりに名前付き関数を使用する場合、重複する同一のイベントリスナーは破棄され、それらを心配する必要はありません。
Syknapse

リスナーの重複が心配な場合は、現在のイベントリスナーを削除してから再度追加してください。完璧ではありませんが、簡単です。
Jacksonkr

回答:


49

動的にアタッチされたイベントリスナーが存在するかどうかを確認する方法はありません。

イベントリスナーが接続されているかどうかを確認する唯一の方法は、次のようにイベントリスナーを接続することです。

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

その後onclick、戻る!!elem.onclick(または同様の)ことにより、イベントリスナーが接続されているかどうかをテストできます。


33

私はそのようなことをしました:

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

リスナーがアタッチされているときに要素の特別な属性を作成し、それが存在するかどうかを確認します。


4
これは、私が過去に使用したアプローチです。私の推奨は、非常に具体的な構文構造を使用することです。IEは「リスナー」の代わりに、イベント自体を使用します。つまり、「data-event-click」です。これにより、複数のイベントを実行するイベントにある程度の柔軟性が提供され、物事が少し読みやすくなります。
conrad10781

これに@ conrad10781を追加すると、最も信頼性が高く、最も簡単な方法のようです。何らかの理由で要素が再レンダリングされ、イベントリスナーが切断された場合、この属性もリセットされます。
Arye Eidelman

23

私がやろうとしていることは、関数の外側にブール値を作成することです。ブール値は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


6

tl; dr:いいえ、ネイティブでサポートされている方法でこれを行うことはできません。


これを達成するために私が知っている唯一の方法は、追加されたリスナーのレコードを保持するカスタムストレージオブジェクトを作成することです。次の行に沿って何か:

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

ステップ1:最初に、ストレージオブジェクトをトラバースし、指定された要素(またはfalse)の要素のレコードを返すことができる関数が必要です。

/* 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年以上経過していますが、将来この問題に遭遇した人はこの回答の恩恵を受けると思いますので、遠慮なく提案または改善してください。😊


この解決策をありがとう、それは簡単で頑丈です。ただし、1つの注意点があります。関数「removeListener」は、イベント配列が空であるかどうかをチェックしますが、3項は正しくありません。「if(record.listeners [event] .length!=-1)」と表示されているはずです。
JPA

5

たとえば、Chromeインスペクターなどを使用して、EventListenerが存在するかどうかをいつでも手動で確認できます。「要素」タブには、従来の「スタイル」サブタブがあり、その近くに別のタブ「イベントリスナー」があります。リンクされた要素を含むすべてのEventListenerのリストが表示されます。


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
要素にプロパティを割り当てることもできます。要素は基本的に、イベントが関連付けられている要素としてマークします。
ジャスティン

2

特定の要素の下に登録されたイベントを検索するクロスブラウザー機能はないようです。

ただし、一部のブラウザでは、開発ツールを使用して要素のコールバック関数を表示できます。これは、Webページがどのように機能するかを判断する場合や、コードをデバッグする場合に役立ちます。

Firefox

まず、開発者ツールの[ インスペクター ]タブで要素を表示します。これを行うことができます:

  • ページ上検査するWebページ上のアイテムを右クリックして、メニューから[要素の検査]を選択することにより上で。
  • コンソール内で、関数を使用してdocument.querySelectorなどの要素を選択し、要素の横にあるアイコンをクリックして[ インスペクター ]タブに表示します。

要素にイベントが登録されている場合は、要素の横に「Event」という単語を含むボタンが表示されます。クリックすると、要素に登録されているイベントを確認できます。イベントの横の矢印をクリックすると、そのイベントのコールバック関数を表示できます。

クロム

まず、開発者ツールの[ 要素 ]タブで要素を表示します。これを行うことができます:

  • 検査したいウェブページ上のアイテムを右クリックして、メニューから「検査」を選択することにより、ページ上で
  • コンソールで関数を使用してdocument.querySelectorなどの要素を選択し、要素を右クリックして[要素パネルで表示]を選択して、[ インスペクター ]タブに要素を表示します。

Webページ要素を含むツリーを表示するウィンドウのセクションの近くに、「イベントリスナー」というタイトルのタブが付いた別のセクションがあります。選択すると、要素に登録されたイベントが表示されます。特定のイベントのコードを表示するには、その右側のリンクをクリックします。

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

それは「真」です

したがって、「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);

これは良いトリックですが、現在「追加されている」イベントリスナーの1つである関数への参照がある場合にのみ機能すると思います。これは明らかに標準APIの欠点だと思います。イベントリスナーを追加できる場合は、これまでに追加されたイベントリスナーを確認することもできます。イベントリスナーは現在「変数のみを書き込む」のとほとんど同じで、あいまいなコンセプトのようです。
Panu Logic

0

これを実現するためのスクリプトを書いたところです。それはあなたに2つのグローバル機能を提供します:hasEvent(Node elm, String event)そしてgetEvents(Node elm)あなたはそれを利用することができます。これはEventTargetプロトタイプメソッドを変更し、add/RemoveEventListenerHTMLマークアップまたはJavaScript構文で追加されたイベントでは機能しないことに注意してください。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

理論的には、addEventListenerとremoveEventListenerを便乗させて、「this」オブジェクトにフラグを削除することができます。醜いと私はテストしていません...

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.