I'm making a bookmarklet, but I've encountered some wierd behaviour in IE8. The code causing the problem is this:
var els = document.getElementById("my_id").getElementsByTagName("*");
for(var i in els)
{
alert(i+","+els[i])
}
The first thing that is alerted is "length, n". This isn't the case in chrome: just in IE8.
Interestingly, it seems to behave differently depending on whether the code goes in the console/address bar or the page itself.
Is this standard behaviour?
EDIT:
Not down to the website that I run it on either. Is it possible that getElementsByTagName returns an array with a "length" key set in IE? It certainly doesn't return a pure array.
What you get is not an array, it is a nodeList.
See here, click the link "getElementsByTagName('element_name')"
Furthermore, for..in is not meant to be used for iterating over an array anyway. Use
var item;
for (var idx=0, len=arr.length; idx<len; ++idx) {
item = arr[idx];
// whatever
}
for array iteration.
For a nodelist, you can get the element with
list.item(idx); // where list is what you got from getElementsByTagName
if you want to do it "right".
The IE behavior is correct. It may return all kinds of weird stuff, since for .. in iterates over all member names of the object.
getElementsByTagName returns a NodeList, and the properties of a NodeList are specified in the DOM standard: length and item(i). Most JavaScript implementations will also allow [i].
Therefore, you should write the following:
var els = document.getElementById("my_id").getElementsByTagName("*");
for (var i = 0;i < els.length;i++)
{
alert(i+","+els.item(i));
}
Do not use for..in for anything other than enumerating properties of an object (unless you like surprises!) Just use a simple for loop for arrays and nodelists.
Here's how I iterate over lists. This way, you won't have to write an extra line in the loop, such as var el = els[i].
var els = document.getElementById("my_id").getElementsByTagName("*");
for (var i=0, el; el=els[i]; i++) {
// do what you like here
}
Not unless you define your own iterator for NodeLists, which is JavaScript (Mozilla)-only. [Here's an implementation] of one I have created for my js-iterators repo.
A method to convert an object, like a node list, arguments object, or custom object to an array of its members is often useful.
Array.from= function(what){
var L, A= [];
if(!what) what= {};
L= what.length;
if(typeof L== 'number'){
while(L) A[--L]= what[L];
return A;
}
for(var p in what){
if(what.hasOwnProperty(p)) A[A.length]= what[p];
}
return A;
}
var N=Array.from(document.getElementById("my_id").getElementsByTagName("*"))
while(N.length){
tem=N.shift();
}
Related
If the code is this:
arr=Array("a","b","c");
for(i in arr);
{
alert(i);
}
there is no any alert,but if it is this:
arr=new Array("a","b","c");
for(i in arr)
{
alert(i);//alerts 0,1,2
}
What is the reason?
Array is a constructor. To create a new object you should be using the new operator to create the object, which the constructor is then bound to and run against. In this case though, it actually should work either way, your issue is most likely related to the semicolons next to your for-loop, as noted in the comments.
As an aside, for creating a new array its generally advised to use the simpler notation
var arr = ["a","b","c"];
Its also questionable to use a for-in loop with an array in javascript, as that will hit any additional properties defined on the array. (so if you said arr.x = 2 it would also iterate over x.
Better to use the iterative form
var i =0, length =arr.length;
for ( ;i<length; i++) {
alert(arr[i]);
}
The reason you're getting different results is that you were using incorrect syntax for your for/in loops.
for(i in arr);
{
alert(i);
}
should not have the first semicolon.
Also note that a nicer way to iterate over an array would be:
arr.forEach(function(value, index){
alert(value); // or alert(index);
});
As bfavaretto has mentioned.
Invoking the Array function without the new keyword will Create and return a new Array object in the same way it would had you used the new keyword.
So these two will alert the same things:
arr1 = new Array("a","b","c");
arr2 = Array("a","b","c");
I have the following code in my web app's main JavaScript:
// uniq for arrays
if (!Array.prototype.getUnique) {
Array.prototype.getUnique = function () {
var u = {}, a = [];
for (var i = 0, l = this.length; i < l; ++i) {
if (u.hasOwnProperty(this[i])) {
continue;
}
a.push(this[i]);
u[this[i]] = 1;
}
return a;
}
}
It's a simple uniq clone for javascript, monkeypatched into the basic Array class. (Not here to debate monkeypatching, flame elsewhere please...)
getUnique() works as intended, but now whenever I iterate over an Array with a for...in loop, an additional index called getUnique is passed to the iterator body, and when this false n is looked up, getUnique's code is the value. (In other words: for...in is looping over the array as it should, but appending a getUnique as the last iteration)
What is going on here? There's another function monkeypatched in right above this one that works fine (indexOf()) and does not appear in the iterator. Here is some example code that triggers this issue:
for (var r in result) {
//tags = tags + result[r]["tags"].split(" ").join(", ")
if (result[r]["tags"]) {
var newtags = result[r]["tags"].split(" ");
debug.log(newtags);
for (var n in newtags) {
debug.log("n is " + n);
tags.push(newtags[n]);
}
}
}
The debug output from this snippet looks like:
[14:22:26.090] [["camp", "furnitur", "wood"]]
[14:22:26.093] ["n is 0"]
[14:22:26.096] ["n is 1"]
[14:22:26.099] ["n is 2"]
[14:22:26.101] ["n is getUnique"]
What is going on here? Refactoring the monkeypatch and dropping it in a utility class is easy enough, but this is a really strange edge case to me. I have some ideas in my head about what is happening here, but those are just guesses. Can anyone explain?
The common misconception is that the for...in operation is, in javascript, made to iterate Arrays. This is wrong. It's usage is to iterate over any enumerable property of an object. In javascript, everything is an object (Arrays included). But you may notice, when you do for...in on an array, you don't get values out like length, slice, etc. This is because those are NOT enumerable properties of the object.
This SO question has some really good information on the usage of for...in: Why is using "for...in" with array iteration a bad idea?
If you want to stick to using for...in, you can do like was suggested above and check for hasOwnProperty inside your for...in
for ( var v in myArray ) {
if ( myArray.hasOwnProperty(v) ) {
// Do Something
}
}
I, however, suggest using plain old boring loops...
for ( var i = 0; i <= myArray.length - 1; i++ ) {
// Do Something
}
Without going into crazy detail, this works without hitting your added methods because the "indexes" of the array are not really indexes at all, but are instead properties with a name that matches their corresponding index: i.e 1, 0, 4, etc.
They feel like indexes because in javascript you can't access a property with dot notation if that property is a number (i.e: myArray.0 will not work). So you do myArray[0], and that feels like an array.
This is what hasOwnProperty is designed to solve: it tells you whether the name you've iterated to is on the object itself, or is inherited from the prototype.
Try this:
for (var n in newtags) {
if (newtags.hasOwnProperty(n)) {
debug.log("n is " + n);
tags.push(newtags[n]);
}
}
You shouldn't iterate over an array using for…in loop, that is intended for enumerating properties of objects. So, for instance, you have no guarantee about the order of the indexes, neither – as you saw – that you're iterate only numeric indexes. If someone else adds a property to the Array's prototype in such way, you will iterate that property as well. See https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in to have the complete picture.
However, in your case, you can do the follow:
for (var n in newtags) {
if (newtags.hasOwnProperty(n)) {
debug.log("n is " + n);
tags.push(newtags[n]);
}
}
That also prevent to iterate properties that are added by other scripts to the prototype. Also, you could define your function as not enumerable (in the browser where ES5 is supported):
Object.defineProperty(Array.prototype, "getUnique", {
value: function () { /*.. your function ..*/}
})
See : https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
Don't use for-in for iteration of numeric properties.
Use a for loop:
for (var i = 0; i < newtags.length; i++) {
The for-in statement is almost never the right tool for this job, because...
It includes all properties, including non-numeric, out of range, and prototyped properties.
It makes no guarantee of the order of enumeration.
While you could do a hasOwnProperty check, there's no real benefit, and has some down-sides...
It slows down your iteration
It doesn't help with the order of enumeration issue
Sometimes you want a prototyped index (rare, but it happens). Using hasOwnProperty makes this impossible.
Taking a JavaScript object with 4 properties:
function Object() {
this.prop1;
this.prop2;
this.prop3;
this.prop4;
}
var obj = new Object();
I use a for(in) loop to inspect each property since I don't know the number or name of the properties:
for(property in obj) {
var prop = obj[property];
}
However I would like to process the properties starting with the last (prop4 in this example). I suppose I would like a reverse-for-in-loop.
How can I do this?
Thanks,
Jack
Adding: The object I am referring to is the one returned from JSON.parse. The properties seem to be consistently ordered. There is no keys() method.
A for (x in y) does not process the properties in any specific order so you cannot count on any desired order.
If you need to process the properties in a specific order, you will need to get all the properties into an array, sort the array appropriately and then use those keys in the desired order.
Using ES5 (or an ES5 shim), you can get all properties into an array with:
var keys = Object.keys(obj);
You could then sort them either in standard lexical order or sort with your own custom function:
keys.sort(fn);
And, then you could access them in your desired order:
for (var i = 0; i < keys.length; i++) {
// process obj[keys[i]]
}
The ECMAScript standard does not define an order to iteration for for in loops. You will want an array, if your datatypes are to be sorted.
Arrays are ordered objects. Properties in objects are inherently unordered. However, if you have some specific reason that you want to work from back to front of whatever the for..in construct would have produced, you could do:
var arr = [];
for (prop in obj) {
arr.push(prop);
}
for (var n=arr.length; n--; ){
var prop = obj[arr[n]];
}
So I am using this in IE8:
var hi=["hi", "lo", "foo", "bar"];
for(i in hi){console.log(i)};
//WTF is that indexOf i value?
LOG: 0
LOG: 1
LOG: 2
LOG: 3
LOG: indexOf
undefined
In chrome and others, I'll just get 0-3, no mysterious "indexOf" thing. Why and what's the fix?
Don't use for...in for arrays. It's best to use the traditional for loop in that case.
The reason is because for...in looks at the array as an object, and therefore properties like indexOf or length may be included in the loop. The normal for loop only deals with numeric keys, so this problem is avoided.
On a side note, unwanted properties could show up when iterating over plain objects as well (as others have noted, properties you add to the object's prototype will show up). You can get around this by writing your for...in loops this way:
var obj = { ... };
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
var item = obj[prop];
...
}
}
To be clear though: you still shouldn't use this method on arrays.
You're using the wrong type of loop for an array - for ... in ... will also include any enumerable properties of the object, which in your case includes the .indexOf() method.
Use this instead:
var i, n = hi.length;
for (i = 0; i < n; ++i) {
console.log(i, hi[i]);
}
Chrome and other up to date browsers implement ECMAScript 5, and correctly mark all built-in methods as non-enumerable properties.
This is happening because a script you are including on your page is adding the indexOf method to Array.prototype. This means all arrays inherit an indexOf method, which is good, since it means you can use that method even in IE8.
But, since there is no way to mark a property as non-enumerable in IE8, you will end up seeing it every time you enumerate over all the properties of the array, which is what you do in a for-in loop. You probably wanted a for loop instead.
I have a string that has data separated by a pipe character (|).
Example
var somestring = "data1|data2|data3";
var separated = somestring.split("|");
I know how to use the split() to separate each data.
However, I don't know how many pipes there will be in the resulting Array.
In jQuery or JavaScript, how do I loop over the array returned?
In jQuery or JavaScript, how do I loop through each separated variable?
You basically just need to iterate over the resulting Array.
jQuery
$.each loop
This method is easy to work with, and benefits in the variables used being encapsulated.
$.each(separated, function(index, chunk) {
// `chunk` is each member of the array.
});
jsFiddle.
Of course, jQuery is JavaScript so any of the below methods will also work.
JavaScript
for loop
This is the recommended way.
for (var i = 0, length = separated.length; i < length; i++) {
var chunk = separated[i];
// `chunk` is each member of the array.
}
jsFiddle.
You'll notice too the length property is cached so it is not looked up on each iteration. Some browsers already optimise for this, however IE appears to still benefit from it cached. It only takes 5 seconds to do, so you may as well keep IE users happy too.
You may want to define i and chunk outside of the for loop, because JavaScript has no block scope (unless you're using let), and those variables will exist before (declaration hoisted) and after (no block scope).
for ( in ) loop
This loop is generally not recommended, as it should be used for iterating over object properties only, not array like member properties.
for (var chunk in separated) {
if ( ! separated.hasOwnProperty(chunk)) {
continue;
}
// `separated[chunk]` is each member of the array.
}
jsFiddle.
This loop will loop over all properties up the prototype chain, so hasOwnProperty() must be used. For this reason it is not recommended for arrays.
for ( of ) loop
This loop is standardized in ECMA 6 and is able to loop over NodeLists and iterators.
for (var chunk of separated) {
// `chunk` is each member of the array.
}
jsFiddle
forEach() method
This method is an addition to the ECMA-262 standard. It's not available in IE8, but it can be shimmed relatively easily.
separated.forEach(function(chunk, index) {
// `chunk` is each member of the array.
});
jsFiddle.
Other specialised methods
If you're looking to iterate for a specific goal, it may be useful to use a specialised iterator. Keep in mind these also don't have the best browser support.
filter method
Creates a mew array of the elements which the associated callback returned truthy for.
separated.filter(function(element) {
return +element > 255;
});
reduce method
Creates a new value based on reducing the elements of the array, from left to right.
separated.reduce(function(accumulator, element) {
return accumulator.concat(element);
}, "");
See also the reduceRight method.
map method
Creates a new array, replacing each element with the returned value of the associated callback.
separated.map(function(element) {
return element.substr(0, 1);
});
every method
Returns a boolean value of which is the result of every element in the array passing the test. This method short circuits, i.e. it returns whenever one element's callback doesn't return truthy.
separated.every(function(element) {
return element.substr(0, 1) == "a";
});
some method
Returns a boolean value of which is the result of some element in the array passing the test. This method short circuits, i.e. it returns whenever one element's callback passes the test.
separated.some(function(element) {
return element.substr(0, 1) == "a";
});
separated.length should be all you need.
str.split() returns an array of values, so in your example, since 'separated' is an array, you could:
for (var i=0, len=separated.length; i < len; i++) {
// do something with separated[i]
}
you can do it in jquery like this
$.each(separated,function(key,item){ alert('here is ' + item + ' at position ' + key) })
If your question really is "how do I loop through each separated variable?" then:
for (var i = 0; i < separated.length; i++)
{
//Do something with separated[i];
}
//or (apparently this is deprecated)
for(var a in separated)
{
//Do something with a
}
Loop through with a FOR...NEXT construct like in most other languages:
var somestring = "data1|data2|data3";
var separated = somestring.split("|");
for (i=0 ; i<separated.length; i++) {
document.write(separated[i]);
document.write("<br/>");
}