正如您可能意识到的那样,问题在于,您要同时为同一个元素(您想要的行为)分配多个单击侦听器(您不想要的行为)。有几种方法可以解决这个问题。
最快的解决办法
解决问题的最快方法是
.removeEventListener()
. 通过此操作,可以删除以前的单击侦听器(向
info
数组)在创建设置编辑数据的第二个单击侦听器之前。
在第二个click listener函数的末尾,您将再次重新绑定第一个click listener,使行为恢复正常。
此解决方案的优点
-
这是解决问题的最快方法
这个解决方案的缺点
-
解除绑定和重新绑定事件侦听器可能会使代码难以解释。
-
为了删除事件侦听器,需要提供原始函数(如
.removeEventListener("click", listenerFunction)
. 因为您当前正在使用
anonymous function expression
,您必须将当前位于click listeners内部的函数移到别处并命名它们(以便将它们传递给
removeEventListener
功能
-
实际上还不清楚
哪一个
事件侦听器绑定到
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.
上面的解决方案是解决问题的最快方法,但是有更可靠的方法来确保有效的解决方案。在这个解决方案中,我将整理您的一些代码,以使其更清晰、更容易理解。
此解决方案的优点
-
我们不必解除绑定或重新绑定任何单击侦听器
-
对所发生的事情更容易解释
这个解决方案的缺点
-
它需要更长的时间来实现,因为它需要重新构造更多的代码
解决方案
第一步:跟踪我们是否在编辑
首先,因为我们没有重新绑定点击监听器,所以我们需要跟踪我们正在编辑的内容。让我们创建一个名为
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
,并且通常用作根据布尔变量选择不同结果的快速方法。
我希望这不是太多的信息。我花了一段时间查看了您的代码,真的想帮助您使用最佳实践等等!如果有任何问题,请告诉我!