JavaScript中对DOM元素数组的splice方法

5
var myArray = [];
myArray = document.querySelectorAll('.selected');

当我调用myArray.splice时,它返回undefined。我该怎么避免这种情况?我需要从该数组中删除一些DOM元素。


你能展示一下带有选定类的HTML代码吗? - Rishi
我的myArray是一个Nodelist而不是普通的数组。 - SNAG
我希望你不要想当然地认为从节点列表中删除元素会将它们从文档中删除。你为什么想要从数组中删除元素? - user663031
@torazaburo,我并不是在想象。我只是想从myArray中删除一些元素。 - Maksim Nesterenko
3个回答

7
问题在于querySelectorAll(..)返回的是一个节点列表(NodeList),而不是标准的JS数组。
也许你想要像下面这样的东西:
Array.prototype.slice.call(document.querySelectorAll('.selected'),  <begin>, <end>);

更新

我错过了您试图删除的部分,谢谢@torazaburo。幸运的是,您可以直接在NodeList上应用过滤器,而无需通过数组转换。像下面这样:

var arrayOfNodes = [].filter.call(document.querySelectorAll(".selected"), function(curNodeItem) {
     return shouldCurrentNodeBeRetained(curNodeItem)? true : false;
    //expanded for clarity.    
});

他试图调用 splice 来删除元素,而不是使用 slice 来选择它们。 - user663031

6

querySelectorAll是一个类似于数组的集合<array-like collection>,但它并不是一个数组,因为它不继承自Array.prototype。要将其转换为真正的数组,您可以使用以下方法:slice

var myArray = [].slice.call(document.querySelectorAll('.selected'));

由于Array.prototype.slice是一个有意通用的方法,也就是说它的内部实现不会检查this值是否真的是Array实例,因此可以像这样使用slice。因此,slice可以与任何类似数组的对象一起使用,具有数字索引和length属性。

1

document.querySelectorAll 返回的是一个 NodeList,而不是一个数组。

因此,默认情况下 NodeList 上没有 Splice 方法。

但是你可以为节点列表创建类似的原型方法。

这里有一个可用的 JSFiddle,它直接从 DOM 中删除元素,就像 splice 一样,你可以按照自己的需要进行修改。

var myArray = [];
myArray = document.querySelectorAll('.selected');

//This is a primitive analogue of splice without adding new elements, it will not remove element from NodeList, however will remove it directly from dome, then it will return the resulting array (As Array), because NodeList is unmodifiable;
NodeList.prototype.splice = function(pos, numToRemove){
    var initRemCount = remCount = numToRemove ? numToRemove : 1;
    var removed = [];
    for(var i = 0; i < this.length; i++){
        if(!remCount)
            break;
        var elm = this[i];
        if(i >= pos){
            //elm.parentElement.removeChild(elm); //I commented this out, 'cause you say you dont want to delete members from DOM, uncomment this to do so
            remCount--;
        }
    }
    return [].slice.call(this, pos, pos + initRemCount); 
}

var resultArray = myArray.splice(2, 2);

//This is the Araay already not a NodeList
console.log(resultArray);

1
当你可以简单地将nodelist转换为数组时,几乎没有必要向NodeList原型添加新的例程(特别是可枚举的例程)。此外,OP已经表示他不希望从DOM中删除这些元素。最后,该实现与Array.splice API不同,因为它没有处理添加新元素的其他参数。 - user663031
好的,注意。不好意思我没有提到对象必须保留在DOM中。不过你能先澄清一下你评论的第一部分吗? - Alexander Arutinyants
我的意思是,这个问题几乎总是通过将节点列表转换为数组来处理;然后你就可以直接使用现有的 Array#splice。你的解决方案也可以正常工作,但我怀疑你是否想在 NodeList 上放置所有数组方法,这将是你要走的方向。 - user663031
@torazaburo 我同意。然而MDN几乎在某种程度上建议这样做。你建议我编辑我的解决方案吗? - Alexander Arutinyants
1
我会说 MDN 是“介绍”它,而不是“建议”使用它,并且还提出了“扩展DOM很危险”的警告,但这仍然是一种方法,所以我认为你不必编辑它。 - user663031
好的。非常感谢你的重要评论。很高兴能和这样的专家交流。 - Alexander Arutinyants

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接