Here is my code in a rails view:
这是我在rails视图中的代码:
<div class="lists">
<h1>Your Todo Lists</h1>
<div class="incomplete-list">
<p>This is the incomplete list</p>
<% @incomplete_list.items.each do |item| %>
<div class="incomplete-item">
<%= form_for :item, url: item_path(item), method: :post, html: { class: "item-box" } do |f|%>
<%= f.label item.title %>
<%= f.check_box item.id %>
<% end %>
</div>
<% end %>
</div>
<div class="complete-list">
<p>This is the completed list</p>
<% @complete_list.items.each do |item| %>
<div class="complete-item">
<%= form_for :item, url: item_path(item), method: :post, html: { class: "item-box" } do |f|%>
<%= f.label item.title %>
<%= f.check_box item.id %>
<% end %>
</div>
<% end %>
</div>
</div>
<script>
$(document).ready(function() {
var changeDom = function(itemRow) {
var parentList = itemRow.parent()
itemRow.remove()
// I am so dumb, you can't remove an itemRow first and then ask for its parent. You dumb dumb. It's [] if you
// ask for the parent after you remove it. So you need to set it first with var parent =
if (parentList.attr("class") === "incomplete-list") {
$(".complete-list").append(itemRow)
}
else {
$(".incomplete-list").append(itemRow)
}
}
var itemLists = $(".lists")
itemLists.find(":checkbox").on("change", function(box){
var box = $(this)
var item_number = parseInt(box[0].name.match(/[^[\]]+(?=])/g)[0])
$.ajax( { type: "PUT",
url: "items/" + item_number,
data: { id: item_number },
success: function(text) {
changeDom($(box[0].closest("div")))
}
});
})
})
</script>
It works the first time. I render this page with incomplete-items and complete-items. But after I click them and after they jump from one list to the other...they lose their listeners and no longer work. What does one do?
它第一次工作。我使用不完整的项目和完整项目呈现此页面。但是在我点击它们并且从一个列表跳到另一个列表之后......他们失去了听众而不再工作。一个人做什么?
Also how do I get it so when an incomplete box is checked, it is checked in the other list and when a checked box is in the complete-list, it becomes unchecked in the other.
另外如何检查一个不完整的框,在另一个列表中检查它,当一个复选框在完整列表中时,它在另一个框中变为未选中状态。
2 个解决方案
#1
If you remove a dom element, then yes, it's listeners will be removed too.
如果删除dom元素,那么是的,它的侦听器也将被删除。
There are two things you could do:
你可以做两件事:
-
Your
changeDom
function could be modified so it doesn't remove the element, but rather it should move the element from one list to another. If you move the element, then the listeners will still be bound.您的changeDom函数可以被修改,因此它不会删除元素,而是应该将元素从一个列表移动到另一个列表。如果移动元素,则仍将绑定侦听器。
Look at the
detach
method in jQuery, as a way to move an element, but keep it's listeners bound查看jQuery中的detach方法,作为移动元素的一种方法,但要保持它的侦听器绑定
-
But a better approach, which is more efficent, is to not actually bind the events to each checkbox / row in your list.
但是更好的方法是更实际地将事件绑定到列表中的每个复选框/行。
Instead, use event delegation to bind a click event at a higher level. Which is more efficent, as you are not having to create listeners for each checkbox. Instead there is just one listener for the entire list - but it will still respond to clicks for each checkbox.
相反,使用事件委派来绑定更高级别的单击事件。哪个更有效,因为您不必为每个复选框创建侦听器。相反,整个列表只有一个监听器 - 但它仍然会响应每个复选框的点击次数。
$(".lists").on("change", ":checkboxes", function() { // do stuff });
Because you are listening for events at a higher level, it won't matter if you move the checkbox elements around - no event listeners will be disrupted.
因为您正在侦听更高级别的事件,所以如果移动复选框元素也无关紧要 - 没有事件侦听器会被中断。
Here is some documentation on event delegation:
以下是有关事件委派的一些文档:
#2
The problem is the use of .remove() which will remove the attached event handlers and data
问题是使用.remove()将删除附加的事件处理程序和数据
In addition to the elements themselves, all bound events and jQuery data associated with the elements are removed
除了元素本身之外,还删除了与元素关联的所有绑定事件和jQuery数据
In your case since the element is added to one or another element after the remove, there is no need to call remove()
you can just append it to the target element
在你的情况下,因为在删除之后将元素添加到一个或另一个元素,所以不需要调用remove()就可以将它附加到目标元素
var changeDom = function (itemRow) {
var parentList = itemRow.parent()
if (parentList.attr("class") === "incomplete-list") {
$(".complete-list").append(itemRow)
} else {
$(".incomplete-list").append(itemRow)
}
}
If you still want to follow the same flow, use detach()
如果您仍想遵循相同的流程,请使用detach()
var changeDom = function (itemRow) {
var parentList = itemRow.parent()
itemRow.detach()
if (parentList.hasClass("incomplete-list")) {
$(".complete-list").append(itemRow)
} else {
$(".incomplete-list").append(itemRow)
}
}
#1
If you remove a dom element, then yes, it's listeners will be removed too.
如果删除dom元素,那么是的,它的侦听器也将被删除。
There are two things you could do:
你可以做两件事:
-
Your
changeDom
function could be modified so it doesn't remove the element, but rather it should move the element from one list to another. If you move the element, then the listeners will still be bound.您的changeDom函数可以被修改,因此它不会删除元素,而是应该将元素从一个列表移动到另一个列表。如果移动元素,则仍将绑定侦听器。
Look at the
detach
method in jQuery, as a way to move an element, but keep it's listeners bound查看jQuery中的detach方法,作为移动元素的一种方法,但要保持它的侦听器绑定
-
But a better approach, which is more efficent, is to not actually bind the events to each checkbox / row in your list.
但是更好的方法是更实际地将事件绑定到列表中的每个复选框/行。
Instead, use event delegation to bind a click event at a higher level. Which is more efficent, as you are not having to create listeners for each checkbox. Instead there is just one listener for the entire list - but it will still respond to clicks for each checkbox.
相反,使用事件委派来绑定更高级别的单击事件。哪个更有效,因为您不必为每个复选框创建侦听器。相反,整个列表只有一个监听器 - 但它仍然会响应每个复选框的点击次数。
$(".lists").on("change", ":checkboxes", function() { // do stuff });
Because you are listening for events at a higher level, it won't matter if you move the checkbox elements around - no event listeners will be disrupted.
因为您正在侦听更高级别的事件,所以如果移动复选框元素也无关紧要 - 没有事件侦听器会被中断。
Here is some documentation on event delegation:
以下是有关事件委派的一些文档:
#2
The problem is the use of .remove() which will remove the attached event handlers and data
问题是使用.remove()将删除附加的事件处理程序和数据
In addition to the elements themselves, all bound events and jQuery data associated with the elements are removed
除了元素本身之外,还删除了与元素关联的所有绑定事件和jQuery数据
In your case since the element is added to one or another element after the remove, there is no need to call remove()
you can just append it to the target element
在你的情况下,因为在删除之后将元素添加到一个或另一个元素,所以不需要调用remove()就可以将它附加到目标元素
var changeDom = function (itemRow) {
var parentList = itemRow.parent()
if (parentList.attr("class") === "incomplete-list") {
$(".complete-list").append(itemRow)
} else {
$(".incomplete-list").append(itemRow)
}
}
If you still want to follow the same flow, use detach()
如果您仍想遵循相同的流程,请使用detach()
var changeDom = function (itemRow) {
var parentList = itemRow.parent()
itemRow.detach()
if (parentList.hasClass("incomplete-list")) {
$(".complete-list").append(itemRow)
} else {
$(".incomplete-list").append(itemRow)
}
}