I saw a lot of questions on the site for the same topic ("removeEventListener not working), but I read everything I could find, and none of them resolved my issue. I did find a few that suggested some workarounds (like creating an object in the scope that's responsible for adding and deleting listeners) but none of them really worked for me, or I had no idea how to implement them to my code specifically. This is why I'm allowing myself to post this. If someone finds a post that specifically resolves my problem, I'll remove this.
To put things briefly, I have a HTML table with "contenteditable" set to true on all "td" tags. I have a button under it which calls the deleteMode() function:
function deleteMode(){
let tds = document.getElementsByTagName('td');
if (delete_mode == true){
table.style.border = null;
for(let i = 0; i < tds.length; i++){
tds[i].style.cursor = null;
tds[i].setAttribute("contenteditable", "true");
tds[i].removeEventListener("mouseover", function(){ setDeleteBGC(i); });
tds[i].removeEventListener("mouseout", function(){ removeDeleteBGC(i); });
tds[i].removeEventListener("click", function(){ deleteEvent(i); });
}
delete_mode = false;
}
else{
table.style.border = "solid 5px red";
for(let i = 0; i < tds.length; i++){
tds[i].style.cursor = "pointer";
tds[i].removeAttribute("contenteditable");
tds[i].addEventListener("mouseover", function(){ setDeleteBGC(i); });
tds[i].addEventListener("mouseout", function(){ removeDeleteBGC(i); });
tds[i].addEventListener("click", function(){ deleteEvent(i); });
}
delete_mode = true;
}
}
(delete_mode variable is set to "false" on page load) For my eventListeners, since I had to send a parameter to the function, I used an anonymous function to call the specified function with the parameters. If I understand correctly, this is why there is a problem.
For those that need to see the full code (HTML + Javascript functions that are being called), here is a JS fiddle:
https://jsfiddle.net/67p5kLze/6/
As you can see in the fiddle, as a user "enters" "delete mode", he is able to click to empty cells 4 by 4. However, as he "exits" it, he can still empty them by clicking, and there is still the orange "preview", which I think is because the EventHandlers are not being removed.
Any help is greatly appreciated. Thanks in advance!
Following @Yousaf 's answer, I modified my code to something like this:
function deleteMode(){
let tds = document.getElementsByTagName('td');
if (delete_mode == true){
table.style.border = null;
for(let i = 0; i < tds.length; i++){
tds[i].style.cursor = null;
tds[i].setAttribute("contenteditable", "true");
bindFunc = deleteEvent.bind(null, i);
tds[i].removeEventListener("mouseover", function(){ setDeleteBGC(i); });
tds[i].removeEventListener("mouseout", function(){ removeDeleteBGC(i); });
tds[i].removeEventListener("click", bindFunc);
}
delete_mode = false;
}
else{
table.style.border = "solid 5px red";
for(let i = 0; i < tds.length; i++){
tds[i].style.cursor = "pointer";
tds[i].removeAttribute("contenteditable");
var bindFunc = deleteEvent.bind(null, i);
tds[i].addEventListener("mouseover", function(){ setDeleteBGC(i); });
tds[i].addEventListener("mouseout", function(){ removeDeleteBGC(i); });
tds[i].addEventListener("click", bindFunc);
}
delete_mode = true;
}
function deleteEvent(index){
let tds = document.getElementsByTagName('td');
console.log(index);
index = Math.floor(index/4) * 4;
let selectInCell = tds[index].getElementsByTagName('select');
selectInCell[0].selectedIndex = 0;
tds[index+1].innerHTML = '';
tds[index+2].innerHTML = '';
tds[index+3].innerHTML = '';
}
}
However, its not working, I'm thinking because the addEventListener and removeEventListener are not in a loop. It's the only way my code was different to his.
If anyone sees the mistake I'm doing, I'd appreciate the help!
.removeEventListener()
takes the same callback function as a second argument that was used to add the event listener, i.e. that was passed as a second argument to.addEventListener()
method.In your case, you are using anonymous function, so the one you pass to
.addEventListener()
method is different from the callback function that you have passed to.removeEventListener()
method. Hence, registered event listeners are not removed.Since you mentioned in your question that you used anonymous functions because you need to pass an argument to the event handler function. You can create a function that you want to use as a event handler, then use
Function.prototype.bind()
to get another function, save a reference to this function returned byFunction.prototype.bind()
somewhere and then use this new function as a event handler.Later, when you need to remove the event listeners, you can remove them easily because references of the event handler functions were already saved.
Following code snippet shows an example:
Edit
Here's the demo of your code with the changes suggested in my answer.
P.S: I suggest that you use the second code snippet which is below this one. This is because your code contains a-lot of code duplication and is unnecessarily complicated. I have added this code snippet just to show you how you could remove the event listeners using the approach mentioned in my answer.
A better solution
Your code is unnecessarily complicated. You are adding and removing the event listeners based on whether you are in delete mode or not.
You could free yourself from the responsibility of removing the event listeners and hence solving your original problem, by adding the
mouseover
,mouseout
andclick
event listeners on thetable
element instead of on eachtd
element.Following code snippet shows an example of how you could improve your code, make it more readable and remove code duplication.