代码之家  ›  专栏  ›  技术社区  ›  krupesh Anadkat

为多个事件配置同一按钮

  •  -2
  • krupesh Anadkat  · 技术社区  · 7 年前

    我的Web应用程序中有一个表单按钮(纯香草JS制作),该表单用于从用户获取数据,单击按钮时,该数据将转到HTML表,依此类推。

    要编辑表格行,我已经在表格列(每行)上放置了“编辑”按钮,下面附加的图像将有助于了解以下内容:

    蓝色提交按钮命名为“添加按钮”,当用户点击表中特定行的“编辑”按钮时,相应的数据显示在输入框中,用户可以填写新的详细信息。 到目前为止一切都很好,现在真正的问题开始了:在用户编辑输入字段中的信息后,蓝色的提交按钮用于保存这些更改,并自动在表中显示更改。(这是必需的行为)。

    相反,所发生的是:修改后的行显示在表中,并且在具有相同详细信息的行中创建一个新条目(“AddButton”事件侦听器执行两次,一次用于保存用户所做的更改,一次用于在行中添加新项)。 我已经放置了两次这个事件监听器(同样的名字,bcz我不想单独的btn来保存编辑的信息)。 为了清楚起见,请看代码:

    (这是全局范围中的第一个AddButton事件侦听器-用于向表中添加新条目)

    addbutton.addEventListener(“click”,function(e))。{
    变量对象={
    id:object.count,
    日期:格式日期(indate.value,inmonth.value)
    天:inday.value,
    项目:初始值,
    价格:parseint(inprice.value)
    };
    对象信息推送(obj);
    object.count+=1;
    refreshdom();
    (});
    

    对于行中的“编辑和删除”按钮,我有: modifybtn与表绑定,因此我可以使用它获取click target。

    App Screenshot

    addButton.addEventListener("click", function(e){
        var obj = {
            id : object.count,
            date : formatDate(inDate.value, inMonth.value),
            day : inDay.value,
            item : inItem.value,
            price : parseInt(inPrice.value),
        };
        object.info.push(obj);
        object.count += 1;
        refreshDOM();
    });
    

    modifyBtn.addEventListener('click', function(e){
        if(e.target.classList[0] === 'editInfo'){
            var eid = e.target.getAttribute('data-id');
            var eindex = getIndex(eid);
    
            inDay.value = object.info[eindex]['day'];
            inDate.value = parseInt(object.info[eindex]['date'].substr(0,2));
            inMonth.value = object.info[eindex]["date"].substr(4);
    
            inItem.value = object.info[eindex]['item'];
            inPrice.value = object.info[eindex]['price'];
            addButton.addEventListener("click", function(e){
                object.info[eindex]['day'] = inDay.value;
                object.info[eindex]['date'] = formatDate(inDate.value, inMonth.value);
                object.info[eindex]['item'] = inItem.value;
                object.info[eindex]['price'] = parseInt(inPrice.value);
                refreshDOM();
            });
        }
        if(e.target.classList[0] === 'deleteInfo'){
            var did = e.target.getAttribute('data-id');
            var dindex = getIndex(did);
            console.log(dindex);
            object.info.splice(dindex,1);
            refreshDOM();
        }
    });
    

    1 回复  |  直到 7 年前
        1
  •  1
  •   Sam Holmes    7 年前

    正如您可能意识到的那样,问题在于,您要同时为同一个元素(您想要的行为)分配多个单击侦听器(您不想要的行为)。有几种方法可以解决这个问题。


    最快的解决办法

    解决问题的最快方法是 .removeEventListener() . 通过此操作,可以删除以前的单击侦听器(向 info 数组)在创建设置编辑数据的第二个单击侦听器之前。

    在第二个click listener函数的末尾,您将再次重新绑定第一个click listener,使行为恢复正常。

    此解决方案的优点

    1. 这是解决问题的最快方法

    这个解决方案的缺点

    1. 解除绑定和重新绑定事件侦听器可能会使代码难以解释。
    2. 为了删除事件侦听器,需要提供原始函数(如 .removeEventListener("click", listenerFunction) . 因为您当前正在使用 anonymous function expression ,您必须将当前位于click listeners内部的函数移到别处并命名它们(以便将它们传递给 removeEventListener 功能
    3. 实际上还不清楚 哪一个 事件侦听器绑定到 addButton 任何时候

    解决方案

    我们需要移动函数声明 添加按钮 外部 属于 .addEventListener 给它起个名字。我叫它 addItemClickHandler 但你可以随意称呼它:

    function addItemClickHandler(e) {
        var obj = {
            id : object.count,
            date : formatDate(inDate.value, inMonth.value),
            day : inDay.value,
            item : inItem.value,
            price : parseInt(inPrice.value),
        };
        object.info.push(obj);
        object.count += 1;
        refreshDOM();
    }
    
    addButton.addEventListener("click", addItemClickHandler);
    

    这应该是完全一样的。现在,我们需要移动第二个事件侦听器函数,您正试图将其添加到自己的命名函数中。因为我们只从内部引用函数的名称,所以我们甚至不需要将其移出,只需给它一个名称。我会给你的 editItemClickHandler :

    addButton.removeEventListener("click", addItemClickHandler);
    addButton.addEventListener("click", function editItemClickHandler(e){
        object.info[eindex]['day'] = inDay.value;
        object.info[eindex]['date'] = formatDate(inDate.value, inMonth.value);
        object.info[eindex]['item'] = inItem.value;
        object.info[eindex]['price'] = parseInt(inPrice.value);
        refreshDOM();
    
        addButton.removeEventListener("click", editItemClickHandler);
        addButton.addEventListener("click", addItemClickHandler);
    });
    
    • 如您所见,我们首先移除侦听器 添加项单击处理程序 所以当你点击“添加”按钮时,它不会做任何事情。

    • 然后我们绑定 不同的 单击Listener,我们将其命名为 编辑项单击处理程序 这样我们可以在编辑完成后删除它

    • 我们做所有需要做的编辑

    • 最后,我们 去除 我们创建的新的编辑点击监听器并重新添加原始点击监听器,因此功能恢复正常。


    更强大的修复

    Here is a codepen of your application after applying the following fixes.

    上面的解决方案是解决问题的最快方法,但是有更可靠的方法来确保有效的解决方案。在这个解决方案中,我将整理您的一些代码,以使其更清晰、更容易理解。

    此解决方案的优点

    1. 我们不必解除绑定或重新绑定任何单击侦听器
    2. 对所发生的事情更容易解释

    这个解决方案的缺点

    1. 它需要更长的时间来实现,因为它需要重新构造更多的代码

    解决方案

    第一步:跟踪我们是否在编辑

    首先,因为我们没有重新绑定点击监听器,所以我们需要跟踪我们正在编辑的内容。让我们创建一个名为 editing 就在下面 object :

    var editing = {
        mode: false,
        index: null
    };
    

    这将让我们跟踪是否在编辑任何内容( editing.mode )以及我们正在编辑的项目的索引是什么( editing.index )

    第2步:更新 添加按钮 要使用的事件侦听器 编辑 对象

    接下来,我们需要修改 addButton.addEventListener 使用这个新的 编辑 对象:

    addButton.addEventListener("click", function(e){
        if (editing.mode) {
            var info = object.info[editing.index];
    
            info['day'] = inDay.value;
            info['date'] = formatDate(inDate.value, inMonth.value);
            info['item'] = inItem.value;
            info['price'] = parseInt(inPrice.value);
    
            editing.mode = false;
        } else {
            var obj = {
                id : object.count,
                date : formatDate(inDate.value, inMonth.value),
                day : inDay.value,
                item : inItem.value,
                price : parseInt(inPrice.value),
            };
            object.info.push(obj);
            object.count += 1;
        }
    
        refreshDOM();
    });
    
    • 如果 编辑.模式 true ,当 添加按钮 单击,它将更新值,然后禁用 编辑.模式 把它放回原来的样子

    • 如果 编辑.模式 false ,只需将该项添加到数组中(与以前的代码相同)

    • 无论发生什么,DOM都会刷新

    第3步:更新 modifyBtn 要使用的事件侦听器 编辑 对象

    另外,我注意到您正在使用类来修改编程行为,而不是 data- 属性。在很多情况下,这是可以的,但是对于确定行为的确切用例,建议使用 数据- 属性。我们还应该设置 href 对… # ,因为我们不需要将它们用作链接:

    <td><a href="#" data-id={{id}} data-action="edit">Edit</a> | <a href="#" data-id={{id}} data-action="delete">Delete</a></td>
    

    现在,我已经重组了你的 modifyBtn.addEventListener() 要以不同的方式处理这些方面:

    modifyBtn.addEventListener('click', function(e){
        e.preventDefault();
    
        var el = e.target;
        var id = parseInt(el.dataset.id);
        var index = object.info.findIndex(item => item.id === id);
        var info = object.info[index];
        var action = el.dataset.action;
    
        if (action === "edit") {
            editing.mode = true;
            editing.index = index;
    
            inDay.value = info['day'];
            inDate.value = parseInt(info['date'].substr(0,2));
            inMonth.value = info["date"].substr(4);
            inItem.value = info['item'];
            inPrice.value = info['price'];
        }
    
        if (action === "delete") {
            object.info.splice(index, 1);
        }
    
        refreshDOM();
    });
    
    • 使用 e.preventDefault() 意味着浏览器无法导航到 小精灵 单击链接时的Href

    • 我把重复的代码移到了检索的地方 eid eindex 无论您在这些函数之外执行什么操作(“编辑”或“添加”)。

    • 当我们正在编辑某些内容时,我们设置 编辑 要拥有的对象 enabled: true 不管我们正在编辑的当前项目的索引是什么

    • 而不是使用 object.info[eindex] 每次,我都把它赋给一个变量

    • 你不再需要保留你的 getIndex 功能,您可以使用 Array.prototype.findIndex() 相反

    • 建议的获取方法 数据- 属性将被使用 element.dataset.name 而不是 element.getAttribute()

    • 现在的标准情况是,无论发生什么,DOM都将被刷新。

    步骤4:添加动态表单头

    好吧,太棒了!所以这完全有效。最后一件事我想做的是让它更清楚发生了什么,所以在你的 index.html 在你的 <div class="form-modal"> ,我要为您的 h2 :

    <h2 id="form-header">Add Item</h2>
    

    然后,回到 index.js :

    formHeader = getElement('form-header');
    

    最后在 refreshDOM() :

    formHeader.textContent = editing.mode ? "Edit Item" : "Add Item";
    

    这将更新里面的文本 <h2 id="form-header"></h2> 取决于它是否正在被编辑。这是一个 ternary operator ,并且通常用作根据布尔变量选择不同结果的快速方法。


    我希望这不是太多的信息。我花了一段时间查看了您的代码,真的想帮助您使用最佳实践等等!如果有任何问题,请告诉我!