Làm thế nào để kiểm tra xem liệu trình xử lý sự kiện được đính kèm động có tồn tại hay không?


103

Đây là vấn đề của tôi: bằng cách nào đó có thể kiểm tra sự tồn tại của trình nghe sự kiện được đính kèm động không? Hoặc làm cách nào để kiểm tra trạng thái của thuộc tính "onclick" (?) Trong DOM? Tôi đã tìm kiếm trên Internet giống như Stack Overflow để tìm giải pháp, nhưng không may mắn. Đây là html của tôi:

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

Sau đó, trong Javascript, tôi đính kèm trình xử lý sự kiện được tạo động vào liên kết thứ hai:

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

Mã chạy tốt, nhưng tất cả các nỗ lực của tôi để phát hiện trình nghe đính kèm đó đều thất bại:

// 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 đang ở đây . Nếu bạn nhấp vào "Thêm onclick cho 2" và sau đó "[liên kết 2]", sự kiện sẽ kích hoạt tốt, nhưng "Liên kết thử nghiệm 2" luôn báo cáo sai. Ai đó có thể giúp đỡ?


1
Tôi rất tiếc phải nói, nhưng không thể lấy các ràng buộc sự kiện bằng phương pháp hiện tại của bạn: stackoverflow.com/questions/5296858/…
Ivan

1
bạn có thể làm điều đó bằng cách sử dụng công cụ chrome dev: stackoverflow.com/a/41137585/863115
arufian

Bạn có thể làm điều đó bằng cách tạo bộ nhớ của riêng mình để theo dõi người nghe. Xem câu trả lời của tôi để biết thêm.
Angel Politis

Nếu mục tiêu là ngăn sự kiện đã được thêm vào không được thêm lại thì câu trả lời đúng là ở đây . Về cơ bản, nếu bạn sử dụng một hàm được đặt tên thay vì một hàm ẩn danh, các trình nghe sự kiện trùng lặp sẽ bị loại bỏ và bạn không cần phải lo lắng về chúng.
Syknapse

Nếu bạn lo lắng về việc có trình nghe trùng lặp thì hãy xóa trình nghe sự kiện hiện tại trước khi bạn thêm lại. Nó không hoàn hảo nhưng nó đơn giản.
Jacksonkr

Câu trả lời:


49

Không có cách nào để kiểm tra xem liệu trình lắng nghe sự kiện được đính kèm động có tồn tại hay không.

Cách duy nhất bạn có thể biết liệu một trình lắng nghe sự kiện có được đính kèm hay không là đính kèm trình xử lý sự kiện như sau:

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

Sau đó, bạn có thể kiểm tra xem người nghe sự kiện có được đính kèm hay không onclickbằng cách quay lại !!elem.onclick(hoặc thứ gì đó tương tự).


33

Tôi đã làm một cái gì đó như thế:

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

Tạo thuộc tính đặc biệt cho một phần tử khi người nghe được đính kèm và sau đó kiểm tra xem nó có tồn tại hay không.


4
Đây là cách tiếp cận mà tôi đã sử dụng trong quá khứ. Khuyến nghị của tôi là sử dụng một cấu trúc cú pháp rất cụ thể. IE thay vì "trình nghe", sử dụng chính sự kiện. Vì vậy, "dữ liệu-sự kiện-nhấp chuột". Điều này cung cấp một số tính linh hoạt trong trường hợp bạn muốn thực hiện nhiều sự kiện và giúp mọi thứ dễ đọc hơn một chút.
conrad10781

Điều này với sự bổ sung của @ conrad10781 có vẻ như là cách đáng tin cậy nhất và đơn giản nhất. Nếu vì bất kỳ lý do gì mà phần tử được hiển thị lại và trình xử lý sự kiện bị ngắt kết nối, thì thuộc tính này cũng sẽ được đặt lại.
Arye Eidelman

23

Những gì tôi sẽ làm là tạo một Boolean bên ngoài hàm của bạn, bắt đầu là FALSE và được đặt thành TRUE khi bạn đính kèm sự kiện. Điều này sẽ đóng vai trò như một loại cờ cho bạn trước khi bạn đính kèm lại sự kiện. Đây là một ví dụ về ý tưởng.

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

Điều này đòi hỏi bạn phải thay đổi mã của sự kiện gắn liền với theo dõi xem nó được đính kèm (tương tự như này hoặc câu trả lời này ). Nó sẽ không hoạt động nếu bạn không kiểm soát mã.
user202729


6

tl; dr : Không, bạn không thể làm điều này theo bất kỳ cách nào được hỗ trợ tự nhiên.


Cách duy nhất tôi biết để đạt được điều này là tạo một đối tượng lưu trữ tùy chỉnh nơi bạn lưu giữ hồ sơ về những người nghe được thêm vào. Vài điều dọc theo những dòng sau:

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

Bước 1: Đầu tiên, bạn sẽ cần một hàm có thể duyệt qua đối tượng lưu trữ và trả về bản ghi của một phần tử đã cho phần tử đó (hoặc 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;
}

Bước 2: Sau đó, bạn sẽ cần một chức năng có thể thêm trình nghe sự kiện nhưng cũng chèn trình nghe đó vào đối tượng lưu trữ.

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

Bước 3: Đối với yêu cầu thực tế của câu hỏi của bạn, bạn sẽ cần hàm sau để kiểm tra xem một phần tử đã được thêm trình xử lý sự kiện cho một sự kiện cụ thể hay chưa.

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

Bước 4: Cuối cùng, bạn sẽ cần một chức năng có thể xóa một người nghe khỏi đối tượng lưu trữ.

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

Đoạn trích:


Mặc dù đã hơn 5 năm trôi qua kể từ khi OP đăng câu hỏi, tôi tin rằng những người vấp phải nó trong tương lai sẽ được hưởng lợi từ câu trả lời này, vì vậy hãy thoải mái đưa ra đề xuất hoặc cải thiện nó. 😊


Cảm ơn bạn vì giải pháp này, nó đơn giản và chắc chắn. Tuy nhiên, có một nhận xét, có một sai lầm nhỏ. Hàm "removeListener" kiểm tra xem mảng sự kiện có trống hay không nhưng số ba không chính xác. Nó sẽ nói "if (record.listators [event] .length! = - 1)".
JPA

5

Bạn luôn có thể kiểm tra thủ công xem EventListener của mình có tồn tại hay không bằng cách sử dụng trình kiểm tra Chrome. Trong tab Thành phần, bạn có tab phụ "Kiểu" truyền thống và gần với tab đó một tab khác: "Trình nghe sự kiện". Điều này sẽ cung cấp cho bạn danh sách tất cả Người theo dõi sự kiện với các phần tử được liên kết của họ.


5

Có vẻ kỳ lạ là phương pháp này không tồn tại. Cuối cùng đã đến lúc thêm nó chưa?

Nếu bạn muốn, bạn có thể làm như sau:

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

Đây là một tập lệnh tôi đã sử dụng để kiểm tra sự tồn tại của một trình xử lý sự kiện được đính kèm động. Tôi đã sử dụng jQuery để đính kèm một trình xử lý sự kiện vào một phần tử, sau đó kích hoạt sự kiện đó (trong trường hợp này là sự kiện 'nhấp chuột'). Bằng cách này, tôi có thể truy xuất và nắm bắt các thuộc tính sự kiện sẽ chỉ tồn tại nếu trình xử lý sự kiện được đính kèm.

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
Bạn cũng có thể gán một thuộc tính cho phần tử mà phần tử của bạn về cơ bản đánh dấu nó là phần tử có sự kiện gắn liền với nó;
Justin

2

Dường như không có chức năng trình duyệt chéo nào tìm kiếm các sự kiện được đăng ký trong một phần tử nhất định.

Tuy nhiên, có thể xem các hàm gọi lại cho các phần tử trong một số trình duyệt bằng cách sử dụng các công cụ phát triển của chúng. Điều này có thể hữu ích khi cố gắng xác định cách một trang web hoạt động hoặc để gỡ lỗi mã.

Firefox

Đầu tiên, hãy xem phần tử trong tab Thanh tra trong các công cụ dành cho nhà phát triển. Điều này có thể thực hiện được:

  • Trên trang bằng cách nhấp chuột phải vào mục trên trang web mà bạn muốn kiểm tra và chọn "Kiểm tra phần tử" từ menu.
  • Trong bảng điều khiển bằng cách sử dụng một hàm để chọn phần tử, chẳng hạn như document.querySelector , sau đó bấm vào biểu tượng bên cạnh phần tử để xem phần tử đó trong tab Thanh tra .

Nếu bất kỳ sự kiện nào được đăng ký cho phần tử, bạn sẽ thấy một nút có chứa từ Sự kiện bên cạnh phần tử. Nhấp vào nó sẽ cho phép bạn xem các sự kiện đã được đăng ký với phần tử. Nhấp vào mũi tên bên cạnh một sự kiện cho phép bạn xem chức năng gọi lại cho sự kiện đó.

Trình duyệt Chrome

Đầu tiên, hãy xem phần tử trong tab Phần tử trong công cụ dành cho nhà phát triển. Điều này có thể thực hiện được:

  • Trên trang bằng cách nhấp chuột phải vào mục trên trang web mà bạn muốn kiểm tra và chọn "Kiểm tra" từ menu
  • Trong bảng điều khiển bằng cách sử dụng một chức năng để chọn phần tử, chẳng hạn như document.querySelector , nhấp chuột phải vào phần tử và chọn "Hiển thị trong bảng điều khiển Thành phần" để xem nó trong tab Thanh tra .

Gần phần của cửa sổ hiển thị cây chứa các phần tử của trang web, phải có một phần khác có tab có tên "Trình nghe sự kiện". Chọn nó để xem các sự kiện đã được đăng ký vào phần tử. Để xem mã cho một sự kiện nhất định, hãy nhấp vào liên kết ở bên phải của sự kiện đó.

Trong Chrome, các sự kiện cho một phần tử cũng có thể được tìm thấy bằng cách sử dụng hàm getEventListaries . Tuy nhiên, dựa trên các thử nghiệm của tôi, hàm getEventListaries không liệt kê các sự kiện khi nhiều phần tử được chuyển cho nó. Nếu bạn muốn tìm tất cả các phần tử trên trang có trình nghe và xem các chức năng gọi lại cho những trình nghe đó, bạn có thể sử dụng mã sau trong bảng điều khiển để thực hiện việc này:

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

Vui lòng chỉnh sửa câu trả lời này nếu bạn biết cách thực hiện việc này trong các trình duyệt nhất định hoặc trong các trình duyệt khác.


1

Tôi chỉ phát hiện ra điều này bằng cách thử xem sự kiện của tôi có được đính kèm hay không ....

nếu bạn làm :

item.onclick

nó sẽ trả về "null"

nhưng nếu bạn làm:

item.hasOwnProperty('onclick')

thì nó là "TRUE"

vì vậy tôi nghĩ rằng khi bạn sử dụng "addEventListener" để thêm trình xử lý sự kiện, cách duy nhất để truy cập nó là thông qua "hasOwnProperty". Tôi ước tôi biết tại sao hoặc bằng cách nào nhưng than ôi, sau khi nghiên cứu, tôi vẫn chưa tìm ra lời giải thích.


2
onclicktách biệt .addEventListener- nó ảnh hưởng đến một thuộc tính trong khi .addEventListenerkhông
Zach Saucier

1

Nếu tôi hiểu rõ, bạn chỉ có thể kiểm tra xem một người nghe đã được kiểm tra hay chưa chứ không thể kiểm tra người nghe đó cụ thể là người trình bày nào.

Vì vậy, một số mã đặc biệt sẽ lấp đầy khoảng trống để xử lý luồng mã hóa của bạn. Một phương pháp thực tế sẽ là tạo một statebiến bằng cách sử dụng. Ví dụ: đính kèm trình kiểm tra của người nghe như sau:

var listenerPresent=false

sau đó nếu bạn đặt một bộ lắng nghe, chỉ cần thay đổi giá trị:

listenerPresent=true

thì bên trong callback eventListener của bạn, bạn có thể chỉ định các chức năng cụ thể bên trong và theo cách này, phân phối quyền truy cập vào các chức năng tùy thuộc vào một số trạng thái dưới dạng biến, ví dụ:

accessFirstFunctionality=false
accessSecondFunctionality=true
accessThirdFunctionality=true

1

Chỉ cần xóa sự kiện trước khi thêm:

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

Đó là một mẹo hay, nhưng tôi cho rằng nó chỉ hoạt động nếu bạn có tham chiếu đến hàm là một trong những trình nghe sự kiện hiện được "thêm". Tôi nghĩ đây rõ ràng là một thiếu sót của API tiêu chuẩn, nếu chúng ta có thể thêm bất kỳ trình xử lý sự kiện nào, thì cũng có thể kiểm tra xem những trình xử lý sự kiện nào đã được thêm vào. Nó gần giống như những người nghe sự kiện hiện đang "chỉ viết các biến" có vẻ như là một khái niệm khó hiểu.
Panu Logic

0

Tôi vừa viết một kịch bản cho phép bạn đạt được điều này. Nó cung cấp cho bạn hai chức năng toàn cục: hasEvent(Node elm, String event)và chức năng getEvents(Node elm)nào bạn có thể sử dụng. Lưu ý rằng nó sửa đổi EventTargetphương thức nguyên mẫu add/RemoveEventListenervà không hoạt động đối với các sự kiện được thêm vào thông qua đánh dấu HTML hoặc cú pháp javascript củaelm.on_event = ...

Thông tin thêm tại GitHub

Bản thử trực tiếp

Kịch bản:

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

Về lý thuyết, bạn có thể cõng thêm addEventListener và removeEventListener để thêm remove một cờ cho đối tượng 'this'. Xấu xí và tôi chưa thử nghiệm ...

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.