简体   繁体   English

Javascript“removeEventListener”没有对匿名函数做任何事情,没有简单的方法来做到这一点?

[英]Javascript "removeEventListener" not doing anything with anonymous function, no easy way to do this?

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.我在网站上看到了很多关于同一主题的问题(“removeEventListener 不工作),但我阅读了我能找到的所有内容,但没有一个解决了我的问题。我确实找到了一些建议一些解决方法(比如创建一个对象)在负责添加和删除侦听器的范围内)但它们都没有真正对我有用,或者我不知道如何专门将它们实现到我的代码中。这就是为什么我允许自己发布这个。如果有人发现专门解决我的问题的帖子,我将删除它。


To put things briefly, I have a HTML table with "contenteditable" set to true on all "td" tags.简而言之,我有一个 HTML 表,所有“td”标签上的“contenteditable”都设置为 true。 I have a button under it which calls the deleteMode() function:我在它下面有一个按钮,它调用 deleteMode() 函数:

        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. (delete_mode 变量在页面加载时设置为“false”)对于我的 eventListeners,因为我必须向函数发送一个参数,所以我使用了一个匿名函数来调用带有参数的指定函数。 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:对于那些需要查看完整代码(正在调用的 HTML + Javascript 函数)的人,这里有一个 JS 小提琴:

https://jsfiddle.net/67p5kLze/6/ 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.正如你在小提琴中看到的,当用户“进入”“删除模式”时,他可以点击 4 个 4 来清空单元格。但是,当他“退出”它时,他仍然可以通过点击来清空它们,然后仍然是橙色的“预览”,我认为这是因为 EventHandler 没有被删除。

Any help is greatly appreciated.任何帮助是极大的赞赏。 Thanks in advance!提前致谢!


Following @Yousaf 's answer, I modified my code to something like this:按照@Yousaf 的回答,我将代码修改为如下所示:

        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.但是,它不起作用,我在想是因为 addEventListener 和 removeEventListener 不在循环中。 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, ie that was passed as a second argument to .addEventListener() method. .removeEventListener()采用相同的回调函数作为用于添加事件侦听器的第二个参数,即作为第二个参数传递给.addEventListener()方法。

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.在你的情况,你正在使用匿名函数,所以你传递给一个.addEventListener()方法是从回调函数,你已经通过不同.removeEventListener()方法。 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 by Function.prototype.bind() somewhere and then use this new function as a event handler.您可以创建一个要用作事件处理程序的函数,然后使用Function.prototype.bind()获取另一个函数,将Function.prototype.bind()返回的该函数的引用保存在某处,然后使用这个新Function.prototype.bind()用作事件处理程序。

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:以下代码片段显示了一个示例:

 const btn = document.querySelector('#one'); const removeBtn = document.querySelector('#two'); function handleClick(val) { console.log(val); } // 123 is the argument that we want to pass to the event handler const bindFunc = handleClick.bind(null, 123); btn.addEventListener('click', bindFunc); removeBtn.addEventListener('click', () => { btn.removeEventListener('click', bindFunc) });
 <button id="one">Click</button> <button id="two">Remove Click Listener</button>

Edit编辑

Here's the demo of your code with the changes suggested in my answer.这是您的代码演示,其中包含我的答案中建议的更改。

PS: I suggest that you use the second code snippet which is below this one. PS:我建议您使用此代码段下方的第二个代码段。 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.我添加了此代码片段只是为了向您展示如何使用我的答案中提到的方法删除事件侦听器。

 var delete_mode = false; var table = document.getElementById('main-table'); var setBgcEventListeners = []; var removeBgcEventListeners = []; var deleteEventListeners = []; 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', setBgcEventListeners[i]); tds[i].removeEventListener('mouseout', removeBgcEventListeners[i]); tds[i].removeEventListener('click', deleteEventListeners[i]); } delete_mode = false; } else { table.style.border = 'solid 2px red'; for (let i = 0; i < tds.length; i++) { tds[i].style.cursor = 'pointer'; tds[i].removeAttribute('contenteditable'); let setDelBGC = setDeleteBGC.bind(null, i, tds); let removeDelBGC = removeDeleteBGC.bind(null, i, tds); let removeTdContent = deleteEvent.bind(null, i, tds); setBgcEventListeners.push(setDelBGC); removeBgcEventListeners.push(removeDelBGC); deleteEventListeners.push(removeTdContent) tds[i].addEventListener('mouseover', setDelBGC); tds[i].addEventListener('mouseout', removeDelBGC); tds[i].addEventListener('click', removeTdContent); } delete_mode = true; } } function setDeleteBGC(index, tds) { index = Math.floor(index / 4) * 4; tds[index].style.backgroundColor = 'orange'; tds[index + 1].style.backgroundColor = 'orange'; tds[index + 2].style.backgroundColor = 'orange'; tds[index + 3].style.backgroundColor = 'orange'; } function removeDeleteBGC(index, tds) { index = Math.floor(index / 4) * 4; tds[index].style.backgroundColor = null; tds[index + 1].style.backgroundColor = null; tds[index + 2].style.backgroundColor = null; tds[index + 3].style.backgroundColor = null; } function deleteEvent(index, tds) { index = Math.floor(index / 4) * 4; tds[index].innerHTML = ''; tds[index + 1].innerHTML = ''; tds[index + 2].innerHTML = ''; tds[index + 3].innerHTML = ''; }
 td, tr, table { border: 1px solid black; border-collapse: collapse; }
 <table id="main-table"> <tr> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> </tr> <tr> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> </tr> </table> <button onclick="deleteMode()">Delete Mode</button>

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 and click event listeners on the table element instead of on each td element.通过在table元素上而不是在每个td元素上添加mouseovermouseoutclick事件侦听器,您可以将自己从删除事件侦听器的责任中解放出来,从而解决您的原始问题。

Following code snippet shows an example of how you could improve your code, make it more readable and remove code duplication.以下代码片段显示了如何改进代码、使其更具可读性和删除代码重复的示例。

 var deleteMode = false; var tds = [...document.querySelectorAll('td')]; var deleteBtn = document.getElementById('delBtn'); var table = document.getElementById('main-table'); var hoverColor = 'orange'; const groupLength = 4; table.addEventListener('mouseover', handleEvent); table.addEventListener('mouseout', handleEvent); table.addEventListener('click', handleEvent); deleteBtn.addEventListener('click', function () { deleteMode = !deleteMode; let cursor, border; if (deleteMode) { border = 'solid 2px red'; cursor = 'pointer'; tds.forEach(td => td.removeAttribute('contenteditable')); } else { border = cursor = null; tds.forEach(td => td.setAttribute('contenteditable', 'true')); } table.style.border = border; table.style.cursor = cursor; }); function handleEvent(event) { if (!deleteMode) return; const target = event.target; if (target.matches('td')) { const baseIndex = getTdBaseIndex(target); const eventType = event.type; if (eventType == 'mouseover' || eventType == 'mouseout') { setBgColor(eventType, baseIndex); } else if (eventType == 'click') { deleteTdContent(baseIndex); } } } function changeBgColor(el, color = null) { el.style.backgroundColor = color; } function setBgColor(eventType, baseIndex) { const bgColor = eventType == 'mouseover' ? hoverColor : null; for (let i = baseIndex; i < baseIndex + 4; i++) { changeBgColor(tds[i], bgColor); } } function deleteTdContent(baseIndex) { for (let i = baseIndex; i < baseIndex + 4; i++) { tds[i].innerHTML = ''; } } function getTdBaseIndex(targetTdElm) { let tdIndex = tds.findIndex(td => td == targetTdElm); return Math.floor(tdIndex / groupLength) * groupLength; }
 td, tr, table { border: 1px solid black; border-collapse: collapse; }
 <table id="main-table"> <tr> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> </tr> <tr> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> <td contenteditable="true">Hey</td> </tr> </table> <button id="delBtn">Delete Mode</button>

After intensive research, I finally thought of typing something along the lines of "Remove all eventListerners".经过深入研究,我终于想到了“删除所有事件监听器”之类的东西。 This is impossible to do in plain Javascript but I did find out about the JQuery "on()" and "off()" functions.这在普通的 Javascript 中是不可能做到的,但我确实发现了 JQuery 的“on()”和“off()”函数。

The "on()" function works basically like "addEventListener()" (example in code below) “on()”函数的工作原理基本上类似于“addEventListener()”(下面代码中的示例)

The "off()" function is somewhat different to the "removeEventListener()" though. “off()”函数与“removeEventListener()”有些不同。 Basically, using the "off()" function, you can remove all EventListeners of a specific element, or of all elements of a tag (in my example, the "td" tag).基本上,使用“off()”函数,您可以删除特定元素或标签的所有元素(在我的示例中为“td”标签)的所有事件监听器。 This is what I was trying to acheive.这就是我试图实现的目标。

This is the final form of the function, which now works correctly :这是该函数的最终形式,现在可以正常工作:

        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");
                }
                $( "td" ).off();
                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]).on("click", someNameHere);
                    $(tds[i]).on("mouseover", someNameHere2);
                    $(tds[i]).on("mouseout", someNameHere3);

                    function someNameHere(){ deleteEvent(i); }
                    function someNameHere2(){ setDeleteBGC(i); }
                    function someNameHere3(){ removeDeleteBGC(i); }

                }
                delete_mode = true;
            }
        }

My deleteEvent() function, setDeleteBCG() function, and removeDeleteBCG() functions are fired correctly.我的deleteEvent()函数、 setDeleteBCG()函数和removeDeleteBCG()函数被正确触发。

Note that doing it this way, you don't even have to separate the function from the "on()" declarations.请注意,这样做,您甚至不必将函数与“on()”声明分开。 In other words, my someNameHere functions are somewhat unnecessary, and they could be replaced with anonymous functions.换句话说,我的someNameHere函数有点不必要,可以用匿名函数替换它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM