Javascript / DOM: Làm cách nào để xóa tất cả các sự kiện của một đối tượng DOM?


97

Chỉ cần câu hỏi: Có cách nào để loại bỏ hoàn toàn tất cả các sự kiện của một đối tượng, ví dụ như div không?

CHỈNH SỬA: Tôi đang thêm mỗi div.addEventListener('click',eventReturner(),false);sự kiện.

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

EDIT2: Tôi đã tìm thấy một cách, đang hoạt động, nhưng không thể sử dụng cho trường hợp của tôi:

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

Làm thế nào để bạn đính kèm các sự kiện?
Felix Kling

2
Tiêu đề hỏi về các yếu tố của đối tượng, trong khi câu hỏi thực tế hỏi về các sự kiện . Bạn có muốn xóa các phần tử con hoặc sự kiện không?
Pär Wieslander

oh d * mn, đó là nguyên nhân tôi đã suy nghĩ tại cái gì khác khi tôi đã viết rằng ... tôi quan tâm về sự kiện
Florian Müller

Tôi không biết trường hợp chính xác nhưng trong một số trường hợp, bạn có thể sử dụng các phương thức 'on *' (như div.onclick = function), luôn hoạt động với một trình nghe duy nhất và dễ dàng xóa div.onclick=null. Tất nhiên bạn không nên sử dụng addEventListenerhoàn toàn trong trường hợp này vì nó sẽ thêm một trình lắng nghe riêng biệt khác với trình nghe trong onclick.
venimus

Câu trả lời:


105

Tôi không chắc ý bạn với việc xóa tất cả các sự kiện . Xóa tất cả các trình xử lý cho một loại sự kiện cụ thể hoặc tất cả các trình xử lý sự kiện cho một loại?

Xóa tất cả các trình xử lý sự kiện

Nếu bạn muốn xóa tất cả các trình xử lý sự kiện (thuộc bất kỳ loại nào), bạn có thể sao chép phần tử và thay thế nó bằng bản sao của nó:

var clone = element.cloneNode(true);

Lưu ý: Điều này sẽ bảo toàn các thuộc tính và con, nhưng nó sẽ không bảo toàn bất kỳ thay đổi nào đối với thuộc tính DOM.


Xóa các trình xử lý sự kiện "ẩn danh" thuộc loại cụ thể

Cách khác là sử dụng removeEventListener()nhưng tôi đoán bạn đã thử cách này và nó không hoạt động. Đây là sản phẩm :

Mỗi lần gọi addEventListenerđến một chức năng ẩn danh sẽ tạo ra một người nghe mới. Việc gọi removeEventListenerđến một chức năng ẩn danh không có tác dụng . Một hàm ẩn danh tạo ra một đối tượng duy nhất mỗi khi nó được gọi, nó không phải là một tham chiếu đến một đối tượng hiện có mặc dù nó có thể gọi một đối tượng. Khi thêm trình xử lý sự kiện theo cách này, hãy chắc chắn rằng nó chỉ được thêm một lần, nó là vĩnh viễn (không thể xóa) cho đến khi đối tượng mà nó được thêm vào, bị hủy.

Về cơ bản bạn đang đi qua một chức năng ẩn danh để addEventListenertheo eventReturnerlợi nhuận một hàm.

Bạn có hai khả năng để giải quyết vấn đề này:

  1. Không sử dụng một hàm trả về một hàm. Sử dụng chức năng trực tiếp:

    function handler() {
        dosomething();
    }
    
    div.addEventListener('click',handler,false);
    
  2. Tạo một trình bao bọc để addEventListenerlưu trữ một tham chiếu đến hàm được trả về và tạo một số removeAllEventshàm kỳ lạ :

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

    Và sau đó bạn có thể sử dụng nó với:

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

BẢN GIỚI THIỆU

Lưu ý: Nếu mã của bạn chạy trong một thời gian dài và bạn đang tạo và loại bỏ nhiều phần tử, bạn sẽ phải đảm bảo loại bỏ các phần tử có trong _eventHandlerskhi bạn hủy chúng.


@Florian: Chắc chắn, mã có thể được cải thiện, nhưng nó sẽ cung cấp cho bạn một ý tưởng ... viết mã vui vẻ!
Felix Kling

2
Bạn đã thử điều này với nhiều hơn một phần tử div chưa? nút var thực sự sẽ được chuyển đổi thành một chuỗi, ví dụ: '[object HTMLDivElement]' có nghĩa là bạn sẽ thêm mọi thứ vào cùng một nút.
cstruter

@cstruter: Đúng rồi ... đây là những ngày đầu tôi sử dụng JavaScript ... Tôi sẽ sửa mã khi tôi tìm thấy thêm một thời gian nữa. Cảm ơn vì đã cho tôi biết.
Felix Kling

Tôi đoán có lỗi đánh máy, addListener phải là addEvent
AGamePlayer

Lưu ý rằng element.parentNodecó thể là rỗng. Có lẽ nên đặt một kiểm tra nếu xung quanh đoạn mã đó ở trên.
thecoolmacdude

42

Điều này sẽ loại bỏ tất cả người nghe khỏi trẻ em nhưng sẽ chậm đối với các trang lớn. Viết đơn giản dã man.

element.outerHTML = element.outerHTML;

2
Câu trả lời chính xác. Đối với các nút lá, đây có vẻ là ý tưởng tốt nhất.
user3055938

3
document.querySelector('body').outerHTML = document.querySelector('body').outerHTMLđã làm cho tôi.
Ryan

2
@Ryan thay vì document.querySelector('body').outerHTMLbạn chỉ có thể nhậpdocument.body.outerHTML
Jay Dadhania

28

Sử dụng chức năng riêng của người nghe sự kiện remove(). Ví dụ:

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

2
Không chắc tại sao đây không phải là câu trả lời đúng. Câu hỏi xuất hiện rất nhiều lần trên SO và có thể câu trả lời sẽ xử lý vấn đề bằng cách sử dụng phương pháp nhân bản.
jimasun

42
Có vẻ như điều đó getEventListenerschỉ có sẵn trong các công cụ dành cho nhà phát triển WebKit và trong FireBug. Không phải thứ bạn có thể gọi trong mã của mình.
corwin.amber

1
gặp khó khăn khi làm việc này, bạn chuyển một đối tượng làm đối số cho getEventListeners(). ví dụ: getEventListeners(document)hoặcgetEventListeners(document.querySelector('.someclass'))
Luke

10
Điều này dường như chỉ hoạt động trên bảng điều khiển chrome . Ngay cả trên tập lệnh của bạn trên trang, nó không hoạt động.
Reuel Ribeiro

4
getEventListeners(document).click.forEach((e)=>{e.remove()})tạo ra lỗi này:VM214:1 Uncaught TypeError: e.remove is not a function
Ryan

11

Như corwin.amber đã nói, có sự khác biệt giữa Webkit với những người khác.

Trong Chrome:

getEventListeners(document);

Điều này cung cấp cho bạn một Đối tượng với tất cả các trình nghe sự kiện hiện có:

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

Từ đây, bạn có thể tiếp cận người nghe mà bạn muốn xóa:

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

Vì vậy, tất cả những người nghe sự kiện:

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

Trong Firefox

Có một chút khác biệt vì nó sử dụng trình bao bọc trình nghe không chứa hàm loại bỏ. Bạn phải có được trình nghe mà bạn muốn xóa:

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

Tất cả những người nghe sự kiện:

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

Tôi đã cố gắng vô hiệu hóa tính năng chống sao chép phiền phức của một trang web tin tức.

Thưởng thức!


Đây là câu trả lời tốt nhất để nhắm mục tiêu loại bỏ trình nghe, chẳng hạn như trong trường hợp xóa một trình nghe cụ thể trên nút tài liệu mà không xóa tất cả các trình nghe của nó.
Trevor

Điều này làm việc tốt và câu trả lời tốt nhất. @Jmakuc Cảm ơn rất nhiều.
Thavamani Kasi 14/03/18

5
Firefox đang nói với tôi rằng getEventListenersnó không được xác định?
rovyko

Đây là một giải pháp kém vì getEventListists hiện tại chỉ được Chrome hỗ trợ.
Michael Paccione

1

Bạn có thể thêm một hàm hook để chặn tất cả các cuộc gọi đến addEventHandler. Cái móc sẽ đẩy trình xử lý đến một danh sách có thể được sử dụng để dọn dẹp. Ví dụ,

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

Bạn nên chèn mã này gần đầu trang web chính của bạn (ví dụ index.html:). Trong quá trình dọn dẹp, bạn có thể lặp qua all_handlers và gọi removeEventHandler cho mỗi. Đừng lo lắng về việc gọi removeEventHandler nhiều lần với cùng một chức năng. Nó là vô hại.

Ví dụ,

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

Lưu ý: đối với IE, hãy sử dụng Element thay vì EventTarget, và thay đổi => thành chức năng và nhiều thứ khác.


0

Để hoàn thành câu trả lời, dưới đây là các ví dụ thực tế về việc xóa các sự kiện khi bạn đang truy cập các trang web và không có quyền kiểm soát đối với mã HTML và JavaScript được tạo.

Một số trang web gây phiền nhiễu đang ngăn bạn sao chép-dán tên người dùng trên biểu mẫu đăng nhập, điều này có thể dễ dàng bị bỏ qua nếu onpastesự kiện được thêm với onpaste="return false"thuộc tính HTML. Trong trường hợp này, chúng ta chỉ cần nhấp chuột phải vào trường nhập, chọn "Kiểm tra phần tử" trong trình duyệt như Firefox và loại bỏ thuộc tính HTML.

Tuy nhiên, nếu sự kiện được thêm vào thông qua JavaScript như thế này:

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

Chúng tôi cũng sẽ phải xóa sự kiện thông qua JavaScript:

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

Trong ví dụ của tôi, tôi đã sử dụng ID "lyca_login_mobile_no" vì nó là ID nhập văn bản được sử dụng bởi trang web tôi đang truy cập.

Một cách khác để xóa sự kiện (cũng sẽ xóa tất cả các sự kiện) là xóa nút và tạo một nút mới, giống như chúng ta phải làm nếu addEventListenerđược sử dụng để thêm sự kiện bằng một hàm ẩn danh mà chúng ta không thể xóa removeEventListener. Điều này cũng có thể được thực hiện thông qua bảng điều khiển của trình duyệt bằng cách kiểm tra một phần tử, sao chép mã HTML, xóa mã HTML và sau đó dán mã HTML vào cùng một vị trí.

Nó cũng có thể được thực hiện nhanh hơn và tự động thông qua JavaScript:

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

Cập nhật: nếu ứng dụng web được tạo bằng khung JavaScript như Angular, có vẻ như các giải pháp trước đó không hoạt động hoặc phá vỡ ứng dụng. Một giải pháp khác để cho phép dán là đặt giá trị thông qua JavaScript:

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

Hiện tại, tôi không biết có cách nào để loại bỏ tất cả các sự kiện hạn chế và xác thực biểu mẫu mà không làm hỏng ứng dụng được viết hoàn toàn bằng JavaScript như Angular hay không.


0

Góc có một polyfill cho vấn đề này, bạn có thể kiểm tra. Tôi không hiểu nhiều nhưng có lẽ nó có thể giúp ích.

const REMOVE_ALL_LISTENERS_EVENT_LISTENER = 'removeAllListists';

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

....


Được EVENT_NAME_SYMBOL_REGXcho là đến từ đâu?
Elias Zamaria

0

Bạn có thể thêm một chức năng trợ giúp xóa trình nghe sự kiện chẳng hạn

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

chỉ cần truyền phần tử vào hàm và thế là xong ...


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

//biên tập

Tôi không biết bạn đang sử dụng phương pháp nào để đính kèm các sự kiện. Đối với addEventListenerbạn có thể sử dụng cái này:

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

biết thêm chi tiết


1
Nếu tôi đặt nó thích div.onClick = somethingthì nó đặt một sự kiện onClick, nhưng ở lại trước đó, vì vậy tôi có về trước và một ví dụ null ...
Florian Müller

1
Sẽ không hoạt động nếu trình xử lý được thêm qua addEventListener().
Felix Kling

@Florian: Không, điều đó không đúng. Nếu bạn sử dụng phương pháp thêm trình xử lý sự kiện này, bạn chỉ có thể có một trình xử lý cho một sự kiện. Nhưng nó thực sự tạo ra sự khác biệt nếu bạn sử dụng addEventListener()...
Felix Kling

nhưng tôi đã nhìn thấy nó như thế này, vì vậy nếu tôi đã thêm chức năng trước đó một lần nữa, nó sẽ được gọi là hai lần ... và, như Felix vua nói, đây không phải làm việc với addEventListener ...
Florian Müller

-1

Có thể trình duyệt sẽ làm điều đó cho bạn nếu bạn làm điều gì đó như:

Sao chép divvà các thuộc tính của nó và chèn nó vào trước cái cũ, sau đó chuyển nội dung từ cái cũ sang cái mới và xóa cái cũ?


bây giờ đó thực sự là một ý tưởng hay, nhưng hoàn toàn không hiệu quả, nếu bạn phải làm điều này cho một số lượng lớn các div từ 1'000 đến 1'000'000 ... Vâng, đó là một dự án lớn;)
Florian Müller

6
1M div trong một trang? Bạn sẽ nhận được vào rắc rối khác trước khi cái này;) Có thể sử dụng đoàn sự kiện sau đó ...
Mic

-1

Xóa tất cả các sự kiện trêndocument :

Lót:

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

Phiên bản đẹp:

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

-1

Chỉ Chrome

Vì tôi đang cố gắng xóa EventListener trong thử nghiệm Thước đo góc và không có quyền truy cập vào Listener trong thử nghiệm, nên các thao tác sau sẽ xóa tất cả các trình xử lý sự kiện thuộc một loại duy nhất:

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

Tôi hy vọng điều này sẽ giúp ai đó vì câu trả lời trước đó đã sử dụng phương pháp "loại bỏ" mà kể từ đó không hoạt động nữa.


-1

Một phương pháp là thêm một trình nghe sự kiện mới gọi e.stopIm InstantPropagation ().

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.