admin管理员组

文章数量:1326447

I have a class method which defines the event listeners. Let us use the following code snippet for simplicity.

function bindEvents() {
    document.querySelector('button').addEventListener('click', (e) => {
        console.log('clicked!');
    }); 
}

// Initial event binding
bindEvents();

// Rebind events at some point for dynamically created elements
bindEvents();
<button type="button">Click</button>

I have a class method which defines the event listeners. Let us use the following code snippet for simplicity.

function bindEvents() {
    document.querySelector('button').addEventListener('click', (e) => {
        console.log('clicked!');
    }); 
}

// Initial event binding
bindEvents();

// Rebind events at some point for dynamically created elements
bindEvents();
<button type="button">Click</button>

Everything works fine when using bindEvents() only once, however for example calling it again in ajax callback results in listener executed twice. So this means after second bindEvents(), clicking the button will console.log() twice and so on. Is there a way I can get around this behavior?

I know I can bind the events "dynamically" on the document and check with e.target, but there is a situation where I need mouseenter/mouseleave events and I don't think it's a good idea to always have those eventListeners on the document.

I've read somewhere the following, but it seems false...

The .addEventListener method ensures that the same function reference won't be bound more than once to the same element/event/captures bination.

Also I have played with the options parameter from here https://developer.mozilla/en-US/docs/Web/API/EventTarget/addEventListener, but without success.

Excuse me if this was answered somewhere, but I failed to find answer in SO and the search engines.

UPDATE: Is there a way to overwrite existing eventListeners or old ones should be removed with removeEventListener like kcp suggested below? Is there more elegant solution to this problem at all?

Share Improve this question edited Jul 6, 2021 at 17:54 Brian Tompsett - 汤莱恩 5,89372 gold badges61 silver badges133 bronze badges asked Jan 18, 2017 at 13:39 Nikolay MihaylovNikolay Mihaylov 3,8848 gold badges29 silver badges33 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 5

The .addEventListener method ensures that the same function reference won't be bound more than once to the same element/event/captures bination.

In your case, each time you execute bindEvents() a pletely new handler is passed to the click event listener since you define new function (no matter it looks the same, it is different object). To use the same handler each time you must define it outside bindEvents and pass it by name (by reference). This works as expexted:

function clickHandler(e){
  alert('clicked!');
}

function bindEvents() {
    document.querySelector('button').addEventListener('click', clickHandler); 
}

// Initial event binding
bindEvents();

// Rebind events at some point for dynamically created elements
bindEvents();
<button>click</button>

However with jQuery I use the following approach which allows me to specify that only elements in a specific container (context or ctx) will be bound:

$.fn.bindEvents = function bindEvents(ctx){

	ctx = ctx || this;

	$('button', ctx).on('click', function(event){
		alert(1);
	});
};

$('body').bindEvents();

$('div').bindEvents();
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<button>Click</button>

<div><button>in div </button></div>

In the example above bindEvents is executed twice but the first button is bound only once since it's not in a div. While if you click on the second button it alerts twice because satisfies both contexts.

addEventListener does not overwrite existing event listeners, it simply adds a new one as the method name implies. Existing listeners must be removed using the removeEventListener method.

function onClick($event) {
    console.log('clicked!');
}
function bindEvents() {
    /** Remove event listener first **/
    document.querySelector('button').removeEventListener('click', onClick);
    document.querySelector('button').addEventListener('click', onClick); 
}

removeEventListener docs

Apart from removeEventListener, You can also use Event delegation. Using this mechanism event is handler by attaching event listener to parent element.

var elem = document.querySelector('div');

elem.addEventListener('click', function(e) {
  e = e || event
  var target = e.target;
  if (target.nodeName != 'BUTTON')
    return;

  console.log('clicked ' + target.textContent);
});

//Simulate addition of dynamic elements
setTimeout(function() {
  var newButton = document.createElement('button');
  newButton.innerHTML = 'Click 2';
  elem.appendChild(newButton)
}, 2000)
<div>
  <button type="button">Click</button>
</div>

本文标签: Rebind JavaScript events and addEventListener fires twiceStack Overflow