Array.prototype.each

Subscribe to Array.prototype.each 11 posts, 3 voices

 
dob Scriptwright

Hey there,

I want to have a prototype function similar to the one Mootool uses for objects and arrays.

What I came up with is this:

Array.prototype.each = function(fun) {
	if (typeof fun != "function") return;
	for (var i=0; i<this.length; i++) {
		fun(this[i]);
	}
	return this;
}

var x = ["cat", "dog", "mouse", "bird"];
x.each(function(needle) { alert(needle); });

This works just fine, it'll alert all array elements.

Now I would like to have the same thing for Object Collections, like

var divs = document.getElementsByTagName("div");
divs.each(function(div) { alert(div.textContent); });

Unfortunately, Object.prototype gives me an error.
Also, my Array.each doesn't work on this.

Any ideas?

 
Aquilax Scriptwright


Array.forEach(divs,function(div){alert(div.textContent);});

 
dob Scriptwright

Firefox has this implemented, I know.
But I'm trying to put it into a cross-browser-compatible framework (IE6+,Opera,Safari,FF)

 
Aquilax Scriptwright

Probably the html elements collection isn't an Array or better it doesn't have Array as base class.
I would prefer to implement the forEach method on the Array object


if (!Array.forEach) Array.prototype.forEach=function(array,callback){if (typeof(array)=="function") {callback=array;array=this;} if(array.length) for(var num1=0;num1<array.length;num1++) callback(array[num1]);};

Here another example how to implement the forEach method for the array object

source: http://developer.mozilla.org/en/docs/Core_JavaS...


if (!Array.prototype.forEach)
{
  Array.prototype.forEach = function(fun /*, thisp*/)
  {
    var len = this.length;
    if (typeof fun != "function")
      throw new TypeError();

    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this)
        fun.call(thisp, this[i], i, this);
    }
  };
}

 
Aquilax Scriptwright

Here something more: Array-like objects

http://developer.mozilla.org/en/docs/Core_JavaS...

 
alien_scum Scriptwright

I'm going to have a random guess at what your aiming for. Neither Firefox or IE let you prototype NodeList or HTMLCollenction. It is probably easiest to write a helper function that adds .each everytime you get a new list (added bonus saves having to type document.getElementsByTagName alot,) or just for kicks you could override document.getElementsByTagName with a new function that adds .each before returning.
Like this:

document.getElementsByTagName=(function(){
  var fn=document.getElementsByTagName;
  return function(){
    var res=fn.apply&&fn.apply(document,arguments)||fn($1);
    res.each=function(){return [].each.apply(this,arguments)});
    return res;
  }
})();

It assumes you've written your each function above, and will let your example run. It is not really an ideal solution though, more a fun example of how you can abuse javascript. You would probably be better with just creating another function. eg
function eachTag(tag,fn){
  [].each.call(document.getElementsByTagName(tag),fn);
}
used like so
eachTag('div',function(div) { alert(div.textContent); });

 
dob Scriptwright

Does anybody know how Mootools solved this?

 
Aquilax Scriptwright

Yes, they haven't solved it, they use a "static" method, like the Array.forEach, to do it:

http://docs.mootools.net/Native/Array.js#$each

 
dob Scriptwright

I'm pretty sure they have a prototype:

var list = $$('#idList li');
list.each(function(element) {
 
	var fx = new Fx.Styles(element, {duration:200, wait:false});
 
	element.addEvent('mouseenter', function(){
		fx.start({
			'margin-left': 5,
			'background-color': '#666',
			color: '#ff8'
		});
	});
 
	element.addEvent('mouseleave', function(){
		fx.start({
			'margin-left': 0,
			'background-color': '#333',
			'color': '#888'
		});
	});
 
});

 
Aquilax Scriptwright

The $$ function returns an array, if you use the bookmarklet: javascript:alert(document.getElementsByTagName("div")); you will receive "object HTMLCollection" as reply, instead with javascript:alert($$("div")); you will receive [object HTMLDivElement],[object HTMLDivElement],... as reply.

The first one is the inalterable like-array HTMLCollection, the second one is a normal array, they don't have solved the problem, they just return an array.

 
dob Scriptwright

Oh, finally found this one again.

Your answer explains a lot, but do you have an idea on how to write a function that returns an array of html elements instead of the standard getElementsByTagName?