Javascript 使用queryselectorall、jquery、getelementsbyid、getelementsbyclassname从dom中删除元素
我正在为一个库编写一个函数,它接受一个元素对象并将其从DOM中删除。我可以很好地工作,但是想知道是否有一种方法可以为循环执行单个Javascript 使用queryselectorall、jquery、getelementsbyid、getelementsbyclassname从dom中删除元素,javascript,Javascript,我正在为一个库编写一个函数,它接受一个元素对象并将其从DOM中删除。我可以很好地工作,但是想知道是否有一种方法可以为循环执行单个?我发现NodeLists和HTMLCollections不能使用相同的for循环,因此我构建了一个数组,然后删除具有相同循环的元素 _remove = function(elem) { if(!elem) { return false; } // getElementById if(!elem.length) {
?我发现NodeLists
和HTMLCollections
不能使用相同的for循环,因此我构建了一个数组,然后删除具有相同循环的元素
_remove = function(elem) {
if(!elem) { return false; }
// getElementById
if(!elem.length) {
if(elem.parentNode) {
elem.parentNode.removeChild(elem);
}
// querySelectorAll, jquery, getElementsByClassName, getElementsByTagName
} else {
var elems = [];
for(var j = 0; j<elem.length; j++) {
if(elem[j]) {
elems.push(elem[j]);
}
}
for(var i=0; i<elems.length; i++) {
if(elems[i].parentNode) {
elems[i].parentNode.removeChild(elems[i]);
}
}
return false;
}
}
其中一些函数返回一个活动节点列表,这意味着当您更改DOM时,集合会立即更改以反映这一点。这就是为什么正常的for
循环不起作用的原因:当删除元素[0]
时,集合中的所有其他项都向下移动。然后,当增加索引时,跳过新的元素[0]
解决此问题的最简单方法是从高端向下循环,而不是从0向上循环
for (var j = elem.length-1; j >= 0; j--) {
if (elem[j].parentNode) {
elem[j].parentNode.removeChild(elem[j]);
}
}
出于以下原因,我建议使用更健壮的实现:
如果是传入的jQuery对象,那么应该调用jQuery.remove()
,因为如果不让jQuery删除jQuery操作(如.data()
或.on()
等)所使用的DOM元素,jQuery可能会泄漏数据和事件侦听器的内存
仅仅做if(elem.length)
是不够的,因为它仍然可以是nodeList
或.length
为0
的数组。我将其更改为test,以查看该属性是否确实存在。仅供参考,如果您想要一个更健壮的测试来查看它是否是一个实际的单个DOM节点,那么有一个健壮的函数来实现这一点,尽管我不确定这里是否需要这样做
为了防止动态节点列表从您下方变出,或者子节点和父节点都在列表中(您不想在子节点之前删除父节点),最好从后向前迭代节点列表(您不必制作副本)
如果这只是一个普通的DOM元素数组,不一定按文档顺序排列,并且列表中有父元素/子元素,或者有人向您传递了一个包含任何旧DOM元素的数组,那么您可能会在.removeChild()上遇到异常
操作,因此最好捕获可能发生的任何异常,以便操作始终可以在整个列表中完成,即使列表中有不正确的元素
您的代码没有一致的返回值。它有一条路径没有返回任何东西。我将其更改为返回false
,如果elem
中没有任何内容是错误的,则返回true
。显然,您可以根据需要修改它,但是如果您想要有一个有意义的返回值,那么所有代码路径都应该有一个返回值(如果它是单个DOM节点,则没有)
推荐代码:
// _remove(elem) - remove one or more DOM nodes from their DOM hierarchy
// elem can be an Array of DOM nodes or a pseudo-array like a nodeList
// or elem can be a single DOM element
function _remove(elem) {
if (!elem) {
return false;
} else if (typeof elem.jquery === "string" && typeof elem.remove === "function") {
// if a jQuery object, it should be removed with jQuery
// so jQuery data and event listener stuff will get cleaned up
// there are more comprehensive ways to check for a jQuery object,
// but those are probably not needed here
elem.remove();
} else if (elem.nodeType) {
// single DOM node
// could also do more comprehensive test to see if it's really a DOM node,
// but probably not needed
if (elem.parentNode) {
elem.parentNode.removeChild(elem);
}
} else if (typeof elem.length === "number") {
// array or pseudo-array here
// querySelectorAll, getElementsByClassName, getElementsByTagName or
// an array of DOM elements assembled by any other code
// iterate backwards so if it is a dynamic nodeList, then children will be
// be removed before parents and any nodeList changes will happen after our
// iteration point
for (var i = elem.length - 1; i >= 0; i--) {
// catch errors in case anything has already been deleted
// from a prior parent delete so we don't abort early
try {
elem[i].parentNode.removeChild(elem[i]);
} catch(e) { }
}
} else {
// don't know what was passed here - not something we were expecting
return false;
}
return true;
}
以相反的方式迭代,两种类型的列表都会涉及到你。你能举个例子吗?我使用了这两种类型的循环,因为当节点列表单独处理第二个循环时,HTMLCollections会在循环运行时删除减少I的项…因此集合中有一半的项没有被删除开始时,将I
作为最后一个索引,而不是第一个索引,和递减而不是递增。gEBTN
和gEBCN
返回实时列表,但qSA
不返回。不删除父元素,这只是本地js删除元素的方式,选择其父元素,然后删除parentNode中的childtypo,因此不允许我编辑,但除此之外效果很好,谢谢@jfriend00:我不认为这会是一个问题,因为文档顺序决定了深度优先顺序,所以反过来说,孩子总是在父母之前被移除。如果我错了,你能举个例子吗?@jfriend00这会是个什么问题?不过我认为您需要处理重复项。您应该删除elem.length==“undefined”
部分的类型,或者以其他方式对其进行返工。某些元素既是元素又是集合,例如
和
元素分别具有.length
和引用其输入和选项的数字索引。因此,现在它将进入下一个条件,并删除列表项而不是元素……但是为了安全起见,使用jQuery的.remove()
是一个很好的方法。@斜视-关于.length
的一个很好的方法。我删除了该检查,只检查.nodeType
属性。
// _remove(elem) - remove one or more DOM nodes from their DOM hierarchy
// elem can be an Array of DOM nodes or a pseudo-array like a nodeList
// or elem can be a single DOM element
function _remove(elem) {
if (!elem) {
return false;
} else if (typeof elem.jquery === "string" && typeof elem.remove === "function") {
// if a jQuery object, it should be removed with jQuery
// so jQuery data and event listener stuff will get cleaned up
// there are more comprehensive ways to check for a jQuery object,
// but those are probably not needed here
elem.remove();
} else if (elem.nodeType) {
// single DOM node
// could also do more comprehensive test to see if it's really a DOM node,
// but probably not needed
if (elem.parentNode) {
elem.parentNode.removeChild(elem);
}
} else if (typeof elem.length === "number") {
// array or pseudo-array here
// querySelectorAll, getElementsByClassName, getElementsByTagName or
// an array of DOM elements assembled by any other code
// iterate backwards so if it is a dynamic nodeList, then children will be
// be removed before parents and any nodeList changes will happen after our
// iteration point
for (var i = elem.length - 1; i >= 0; i--) {
// catch errors in case anything has already been deleted
// from a prior parent delete so we don't abort early
try {
elem[i].parentNode.removeChild(elem[i]);
} catch(e) { }
}
} else {
// don't know what was passed here - not something we were expecting
return false;
}
return true;
}