在JavaScript中合并两个对象(NodeList)数组

时间:2013-02-02 18:55:41

标签: javascript arrays object merge

我正在尝试合并两个对象数组,以便我可以验证表单。通常的concat方法似乎在这种情况下不起作用。 Concat适用于普通的数字和字符串数组,但不适用于对象数组。第var allTags = allInputs.concat(allSelects);行不起作用。

var allInputs = document.getElementsByTagName("input");
alert("Inputs: " + allInputs.length);

var allSelects = document.getElementsByTagName("select");
alert("Selects: " + allSelects.length);

var allTags = allInputs.concat(allSelects);
alert("allTags: " + allTags.length);

5 个答案:

答案 0 :(得分:19)

  

Concat适用于普通的数字和字符串数组,但不适用于对象数组。

实际上确实如此,但NodeList个实例没有concat方法,而Array#concat没有办法确定你想要展平那些(因为它们“不是阵列。”

但是仍然相当容易(但请参见下面的警告)。改变这一行:

var allTags = allInputs.concat(allSelects);

var allTags = [];
allTags.push.apply(allTags, allInputs);
allTags.push.apply(allTags, allSelects);

Live Example | Source

通过使用一些技巧起作用:Array#push接受要添加到数组的可变数量的元素,Function#apply使用this的给定值调用该函数(在我们的例子中,allTags)和任何类似数组的对象作为传递给它的参数。由于NodeList个实例类似于数组,push很乐意将列表中的所有元素推送到数组中。

Function#apply的这种行为(不要求第二个参数确实是一个数组)在规范中明确定义了非常,在现代浏览器中得到了很好的支持。

可悲的是,IE6和7不支持上述内容(我认为它专门使用主机对象 - NodeLists - 用于Function#apply的第二个参数),但是,我们不应该支持他们。 :-) IE8也没有,这更成问题。 IE9很满意。

如果你需要支持IE8及更早版本,遗憾的是,我认为你已经陷入了一个无聊的旧循环:

var allInputs = document.getElementsByTagName('input');
var allSelects = document.getElementsByTagName('select');
var allTags = [];

appendAll(allTags, allInputs);
appendAll(allTags, allSelects);

function appendAll(dest, src) {
  var n;

  for (n = 0; n < src.length; ++n) {
    dest.push(src[n]);
  }

  return dest;
}

Live Example | Source

确实适用于IE8及更早版本(和其他人)。

答案 1 :(得分:2)

document.get[something]会返回一个NodeList,它看起来与Array非常相似,但有许多与众不同的特征,其中两个是:

  • 它不包含concat方法
  • 项目列表是直播。这意味着它们会随着文档的实时更改而发生变化。

如果您将NodeLists转换为实际数组没有任何问题,您可以执行以下操作以达到您想要的效果:

var allInputs  = document.getElementsByTagName("input")
  , allSelects = document.getElementsByTagName("select")
;//nodeLists

var inputList  = makeArray(allInputs)
  , selectList = makeArray(allSelects)
;//nodeArrays

var combined   = inputList.concat(selectList);

function makeArray(list){
  return Array.prototype.slice.call(list);
}

您将丢失原始NodeList的所有行为,包括它能够实时更新以反映DOM的更改,但我的猜测是,这不是真正的问题。

答案 2 :(得分:1)

您正在处理的是HTMLCollection,它们是实时集合(即,当您向DOM添加节点时,它会自动添加到集合中。< / p>

遗憾的是,由于某种原因,它没有concat方法。你需要做的是将它转换为一个数组,失去它的实时行为:

var allInputsArray = [].slice.call(allInputs),
    allSelectsArray = [].slice.call(allSelects),
    allFields = allInputsArray.concat(allSelectsArray);

答案 3 :(得分:0)

根本没有看到下划线.js?你可以做到

 var allSelectsAndInputs = _.union(_.values(document.getElementsByTagName("input")),_.values(document.getElementsByTagName("select")));

有关详细信息,请参阅:http://underscorejs.org/#extend

该功能适用​​于对象,但适用于此设置。

答案 4 :(得分:0)

我找到了一种简单的方法来按照它们在页面或表单上显示的顺序收集dom对象。 首先,在对象上创建一个虚拟类样式'reqd',然后使用以下内容按照它们在页面上显示的顺序创建一个列表。只需将该类添加到您想要收集的任何对象中。

var allTags = document.querySelectorAll("input.reqd, select.reqd");