为多个事件配置相同的按钮

时间:2018-07-17 06:40:06

标签: javascript javascript-events

我的Web应用程序中有一个表单按钮(完全由Vanilla Js制成),该表单用于从用户获取数据,单击按钮时,该数据将进入HTML表等。

要编辑表格行,我在表格列(每行)上放置了“编辑按钮”,下面附上的图片将有助于您清楚主意:

App Screenshot 蓝色的提交按钮名为“ addButton”,当用户单击表中特定行的“编辑”时,相应的数据显示在输入字段中,因此用户可以填写新的详细信息。 到这里一切都很好,现在真正的问题开始了:用户在“输入”字段中编辑了信息之后,“蓝色提交”按钮用于保存这些更改并自发地在表格中显示更改。 (这是必需的行为)。

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

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

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与表格相关,因此我可以通过它获得点击目标。

(存在ModifyBtn第二个addButton事件侦听器内部-用于保存更改并在表中显示输出)

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

所以编辑后,当用户单击蓝色Submit btn时,我只希望执行在ModifyBtn事件监听器内部的addButton事件监听器, 当前,两个addButton事件侦听器都在执行。 很抱歉,无法解释。

1 个答案:

答案 0 :(得分:1)

您可能已经知道,问题在于您正在同时将多个点击侦听器分配给同一元素(所需的行为)(不需要的行为)。有两种方法可以解决此问题。


最快的解决方法

解决问题的最快方法是使用.removeEventListener()。这样,您可以在创建用于设置已编辑数据的第二个单击侦听器之前,删除前一个单击侦听器(向info数组添加新元素的那个)。

在第二个单击侦听器功能结束时,您将再次重新绑定第一个单击侦听器,以使行为恢复正常。

此解决方案的优点

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

此解决方案的缺点

  1. 取消绑定和重新绑定事件侦听器可能使推理代码变得困难
  2. 要删除事件监听器,您需要提供原始功能(例如.removeEventListener("click", listenerFunction)。由于您当前使用的是anonymous function expression,因此必须将当前单击监听器内部的功能移动在其他位置进行命名(以便您可以将它们传递给removeEventListener函数
  3. 实际上并不清楚哪个事件监听器在任何一次都绑定到addButton

解决方案

我们需要将addButton的{​​{1}}的外部的函数声明移开,并为其命名。我称它为.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); }); ,以便当您单击“添加”按钮时,它不会执行任何操作

  • 然后,我们绑定一个不同点击监听器,我们将其命名为addItemClickHandler,以便在编辑完成后将其删除

  • 我们完成了所有需要做的修改

  • 最后,我们删除我们创建的新的编辑点击侦听器,然后重新添加原始的点击侦听器,因此功能可以恢复正常


更强大的修复程序

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

以上解决方案是解决问题的最快方法,但是有更可靠的方法来确保可行的解决方案。在此解决方案中,我将整理您的一些代码,以使其更简洁,更易于理解。

此解决方案的优点

  1. 我们无需取消绑定或重新绑定任何点击监听器
  2. 更容易推断正在发生的事情

此解决方案的缺点

  1. 实施将需要更长的时间,因为它需要重组更多的代码

解决方案

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

首先,由于我们没有重新绑定点击侦听器,因此我们需要跟踪正在编辑的内容。让我们在editItemClickHandler下方创建一个名为editing的对象:

object

这将使我们跟踪是否正在编辑任何内容(var editing = { mode: false, index: null }; ),以及正在编辑的项目的索引是(editing.mode)。

步骤2:更新editing.index事件监听器以使用addButton对象

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

editing
  • 如果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(); }); editing.mode,则单击true时,它将更新值,然后禁用addButton,使其恢复原状。之前

  • 如果editing.modeediting.mode,它将仅将该项添加到数组中(与您之前的代码相同)

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

步骤3:更新false事件监听器以使用modifyBtn对象

此外,我注意到您正在使用类而不是editing属性来修改编程行为。在许多情况下都可以,但是对于确定行为的确切用例,建议改为使用data-属性。我们还应该将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()
  • 使用e.preventDefault()意味着浏览器在单击链接时不会导航到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(); }); href

  • 无论您在这些功能之外执行了什么操作(“编辑”或“添加”),我都将重复的代码移到了#eid的位置

  • 我们现在正在编辑内容时,我们将eindex对象设置为editing,并且无论我们正在编辑的当前项目的索引是什么

  • 我将它分配给了一个变量,而不是每次都使用enabled: true

  • 您不再需要保留object.info[eindex]功能,因为可以使用Array.prototype.findIndex()

  • 获取getIndex属性的推荐方法是使用element.dataset.name而不是data-

  • 按照现在的标准,无论发生什么情况,DOM都会刷新

第4步:添加动态表单标题

好极了!因此,这完全有效。我想做的最后一件事是弄清楚正在发生的事情,因此,在您的element.getAttribute()下的index.html中,我要向您的<div class="form-modal">添加一个ID:

h2

然后,回到<h2 id="form-header">Add Item</h2> 的顶部:

index.js

最后进入formHeader = getElement('form-header');

refreshDOM()

这会将formHeader.textContent = editing.mode ? "Edit Item" : "Add Item"; 中的文本更新为是否要编辑。这是ternary operator,通常用作根据布尔变量选择不同结果的快速方法。


我希望这不是太多信息。我花了一段时间查看您的代码,并真的想为最佳实践提供帮助!让我知道您是否有任何疑问!