15

我正在尝试合并两个对象数组,以便验证表单。在这种情况下,通常的 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);
4

5 回答 5

26

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

实际上,它确实如此,但NodeList实例没有concat方法,Array#concat也没有办法识别您想要展平那些(因为它们不是数组)。

但这仍然很容易做到(不过,请参阅下面的警告)。更改此行:

var allTags = allInputs.concat(allSelects);

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

实例| 资源

这通过使用一些技巧来工作: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;
}

实例| 资源

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

于 2013-02-02T19:02:38.390 回答
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 的更改,但我的猜测是,这不是一个真正的问题。

于 2013-02-02T19:04:11.027 回答
1

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

var allTags = document.querySelectorAll("input.reqd, select.reqd");
于 2013-02-04T22:28:17.327 回答
1

您正在处理的是HTMLCollections,它们是实时集合(即,当您将节点添加到 DOM 时,它会自动添加到集合中。

可悲的是,它没有concat方法……是有原因的。您需要做的是将其转换为数组,从而失去其实时行为:

var allInputsArray = [].slice.call(allInputs),
    allSelectsArray = [].slice.call(allSelects),
    allFields = allInputsArray.concat(allSelectsArray);
于 2013-02-02T19:05:00.350 回答
0

完全查看 underscore.js 吗?你可以做

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

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

该函数适用于对象,但在此设置中有效。

于 2013-02-02T19:03:05.237 回答