最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to iterate through a nodeList functional style - Stack Overflow

programmeradmin4浏览0评论

I frequently want to iterate through a NodeList with forEach or map. My simplified code works like this:

var nodeListMap = Array.prototype.map;
var els = document.querySelectorAll('.classname');
nodeListMap.call(els, function(el){...});

This works fine. However, I'd prefer not have to map.call, but if I do this...

var nodeListMap = Array.prototype.map.call;
var els = document.querySelectorAll('.classname');
nodeListMap(els, function(el){...});

Then it returns

TypeError: object is not a function

How can I modify the code so I simply do nodeListMap(array, fn)?

I frequently want to iterate through a NodeList with forEach or map. My simplified code works like this:

var nodeListMap = Array.prototype.map;
var els = document.querySelectorAll('.classname');
nodeListMap.call(els, function(el){...});

This works fine. However, I'd prefer not have to map.call, but if I do this...

var nodeListMap = Array.prototype.map.call;
var els = document.querySelectorAll('.classname');
nodeListMap(els, function(el){...});

Then it returns

TypeError: object is not a function

How can I modify the code so I simply do nodeListMap(array, fn)?

Share Improve this question edited Feb 8, 2013 at 19:37 methodofaction asked Feb 8, 2013 at 19:23 methodofactionmethodofaction 72.4k22 gold badges158 silver badges167 bronze badges
Add a ment  | 

5 Answers 5

Reset to default 9

I've encountered the same question and my super simple solution with ES6 is:

const els = [...document.querySelectorAll('.classname')]

Such way nodeList bees a regular Array and you can use map, reduce and so on.

Array.prototype.map.call just gets you the call function (Function.prototype.call), without the context. You will need to bind it to the map function:

var nodeListMap = Function.prototype.call.bind(Array.prototype.map);

If you don't want to use bind, you could also write

function nodeListMap(_list /* … */) {
    return Function.prototype.call.apply(Array.prototype.map, arguments);
}

It's probably simplest just to write your own function that "does the right thing"

function map() {
    var args = [].slice.call(arguments, 0);
    var ctx = args.shift();
    return [].map.apply(ctx, args);
}

which will then work for any pseudo-array object.

EDIT this code is updated to ensure that all arguments are passed to .map, even if ECMA add more in the future.

Another option would be to add this functionality to the prototype NodeList:

NodeList.prototype.map = function(step){
    return Array.prototype.map.call(this, step);
};
NodeList.prototype.forEach = function(step){
    return Array.prototype.forEach.call(this, step);
};

With this, you could just call:

els.map(function(el){...});

It should be noted that some people would likely frown upon modifying the prototype of NodeList in this way, but I don't really see a problem with it personally.

Or, if you need to set the this object:

NodeList.prototype.map = function(step){
    return Array.prototype.map.call(this, step, Array.prototype.slice.call(arguments, 1));
};
NodeList.prototype.forEach = function(step){
    return Array.prototype.forEach.call(this, step, Array.prototype.slice.call(arguments, 1));
};

Note: the above has a side effect that when you don't pass a second parameter, this bees an empty array instead of window.

or

NodeList.prototype.map = Array.prototype.map;
NodeList.prototype.forEach = Array.prototype.forEach;

After six years the map and forEach methods are available on NodeLists on most browsers (the odd duck being IE usual). Check forEach nodeList support on caniuse.

If older browser support is needed, I have settled on the following for brevity and clarity (ES6+):

const els = Array.from(document.querySelectorAll('.selector'));

Then, you can simply iterate as...

els.forEach(el => doSomething)

发布评论

评论列表(0)

  1. 暂无评论