There is some strangeness while I create in JavaScript Select element:
var items = {"3":"Three","1":"One","2":"Two"};
var elem = document.createElement("select");
for ( var key in items) {
var ov = document.createElement("option");
ov.value = key;
ov.appendChild(document.createTextNode(items[key]))
elem.appendChild(ov);
}
document.getElementById('someDiv').appendChild(elem);
FF create Select element with initial elements order (Three, One, Two).
Chrome sorts elements by key and output in next way (One, Two, Three).
Why this happen? And how prevent sorting in Chrome?
The for … in statement iterates over an object in an unpredictable order. As MDC says:
A for...in loop iterates over the
properties of an object in an
arbitrary order (see the delete
operator
for more on why one cannot depend on
the seeming orderliness of iteration,
at least in a cross-browser setting).
You'd better use an array of objects and use an ordinary for loop to keep the specified order, like:
var items = [{"3":"Three"}, {"1":"One"}, {"2":"Two"}];
for (var i = 0; i < items.length; i++}
/* etc */ ;
Just a side note: when iterating over object using for … in, you really should use hasOwnProperty to check whether it is an inherited property or not, otherwise you might include inherited properties in your select box.
Related
I need to extract a particular data property from a NodeList. I have no problems getting data from arrays or objects in general, but this NodeList is beyond me! It doesn't work even if I use Array.from() to turn it into an array.
This is the relevant code:
And this is what it logs:
In the log on line 173, the closed arrays contain all the data I need, but I simply don't understand how to go there. When I try to go to an index there it just opens up the path coordinates.
I will also add the code image as text, it doesn't have any lines though:
let test = d3.selectAll(".unit")
console.log(test)
console.log(test._groups)
console.log(test._groups[0])
console.log(test._groups[0][0])
EDIT: To be more specific, the data I need is a "data" property inside the array below the nodelist (?), compare to the previous image of the log on line 173:
EDIT2: To be even more clear: When I open the nodelist in the console, I also get an array, and it is only the array that interests me. I don't understand this data structure, how the array is related to the nodelist, but I have tried to access the indexes of the array in a variety of ways and nothing has worked.
NodeList objects are collections of nodes. In some cases, the NodeList is live, which means that changes in the DOM automatically update the collection.
In other cases, the NodeList is static, where any changes in the DOM does not affect the content of the collection. The document.querySelectorAll() method returns a static NodeList, for instance.
It's possible to loop over the items in a NodeList using a for loop:
for (let i = 0; i < myNodeList.length; i++) {
let item = myNodeList[i];
}
for...of loops will loop over NodeList objects correctly:
const list = document.querySelectorAll('input[type=checkbox]');
for (let checkbox of list) {
checkbox.checked = true;
}
You can use item() method to get an item by its index. For instance, this is how you can get id of the first <div> on some page:
document.querySelectorAll("div").item(0).id
In your case, you have an array and it contains an element of type NodeList.
So, when you do test._groups[0] you get the first element of your array and this element is NodeList and you need to work with it as with NodeList (see above)! For instance:
const arr = [1,2]; // simple array
// arr[0] is 1.
// in your case you have an array, but elements in that array are of type NodeList
// (that's why console shows something like [NodeList(...)])
// when you get something from your array - it will be NodeList
// hence you can iterate over it or get some particular item like
test._groups[0].item(0).ariaAtomic
There are a lot more useful methods. Check docs for more details.
To extrat data from nodeList you must loop your node list and the stock nodList elemnts in otheer tab
var list = node.childNodes;
// Using for..of
for(var value of list.values()) {
console.log(value);
}
[check this link https://developer.mozilla.org/en-US/docs/Web/API/NodeList/values][1]
NodeList objects are collections of nodes.
You can loop through a node list by using the NodeList.length property and read the innerHTML of it as follows.
And refer the document for more knowlege
var items = document.getElementsByTagName("p");
var gross = "";
for (var i = 0; i < items.length; i++) {
gross += items[i].innerHTML;
}
and also you can loop through the
I am trying to copy one array values into another, but without breaking the links that is associated with this array other words i can not just assign the new array to this value, thats why i cannot use methods like slice() or concat().
Here is the code of the function that does the thing:
self.updateBreadcrumbs = function (newBreadcrumbs) {
var old_length = self.breadcrumbs.length;
var new_length =newBreadcrumbs.length;
var j = new_length > old_length ? new_length: old_length;
for (var i = 0; i < j; i++) {
if(old_length < i+1){
self.breadcrumbs.push(newBreadcrumbs[i]);
continue;
}
if(new_length < i+1){
delete self.breadcrumbs[i];
continue;
}
if (self.breadcrumbs[i].title !== newBreadcrumbs[i].title) {
self.breadcrumbs[i] = newBreadcrumbs[i];
}
}
}
My problem is that length of the array does not change when i delete something from the array.
P.S If you know any easier way to do this i am totally open for propositions.
Length of an Array can never change by deleting elements in it.
However It can be altered with splice
eg.
var arr=[1,2,3,4,5]; //length 5
arr.splice(0,1); //length 4
Unlike what common belief suggests, the delete operator has nothing to do with directly freeing memory. delete is only effective on an object's properties. It has no effect on array length
The splice() method changes the content of an array by removing
existing elements and/or adding new elements.
More about Splice
The length property of an Array object is a property that is maintained by it's methods, it is not an inherent characteristic of the object itself. So, if you take an action on it that is not one of Array's own methods, it will not update the length property. In this case, it is the splice method that you would want to use ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice )
Interestingly, you can change the makeup of an array by directly altering its length . . . for example, if you have an array with 6 elements in it and you were to change its length property to 5 (i.e., array.length = 5;), it would no longer recognize the 6th element as being part of the array.
More on the length property: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length
An Array is (aside from its prototype methods) not much more than a simple object. The difference is in the length property which has a spcieal setter method, and the different way of adding a new property.
If a property is added to the object, its name is tested for being a numerical value and whether this value is >= the current array length. if so, the internal length is updated.
If the length is set, it is checked, whether the new value is smaller than the old one, and if so, all own properties with a property name that is a numerical value and >= the new length will be deleted.
If you delete an indexed element from an array, it will just be deleted (and accessing this index later will return undefined, as it would do with any non-existent property). It won't change the array length, nor will increasing the array length create any properties for the added index positions (sparse array). Using [] or new Aray[10000] will both create Arrays with exactly the same memory footprint. Just the length property has a different initial value.
This is completely different from Arrays in other languages, where an array of size n will have n storage spaces reserved for the elements (being pointers to objects or being native value types). So in other languages, creating an array with a given size and filling it is faster than creating an empty array and adding values (in which case each time a larger consecutive space must be allocated and the array must be copied to this larger memory psoition before adding the element). OTOH, accessing the elements of an JS array is somewhat slower (as they are just named properties which have to be located in the property list first) than in other languages where an element is at its given, fixed position.
This answer will not help if you use array, but.
If you still need to delete item by key and you can use object:
$o = {'blue' : '', 'apple' : '', 'red' : ''};
console.log(Object.keys($o).length); // will output 3
delete $o['apple'];
console.log(Object.keys($o).length); // will output 2
I just tried the for...in statement in Javascript.
This gives no error:
var images = document.getElementsByTagName('img');
for(x in images){
document.write(images[x]) + " ");
}
However, this does what it should but gives an error in the FF error console.
for(x in images){
images[x].style.visibility="visible";
}
This made me VERY curious as to what's going on.
Doing this:
for(x in images){
document.write(x);
}
...gave me this:
01234567891011121314151617lengthitemnamedItem
What's there at the end? I assume this makes the document.images / document.getElementsByTagName('img') array not suitable to use with the for...in statement since the values for x at the end won't correspond to an image? Maybe a for loop is better?
Don't iterate through arrays with for ... in loops. Use an index:
for (var i = 0; i < arr.length; ++i) {
// images[i] ...
}
The for ... in construct isn't wrong, it's just not what you want to do; it's for when you want to iterate through all properties of an object. Arrays are objects, and there are other properties besides the semantically-interesting indexed elements.
(Actually what comes back from getElementsByTagName isn't really an Array; it's a node list. You can however treat it like an array and it'll generally work OK. The same basic caveat applies for for ... in in any case.)
for..in does not loop through the indexes of an array, it loops through the enumerable property names of an object. It happens that the only enumerable properties array instances have, by default, are array indexes, and so it mostly works to think it does array indexes in limited situations. But that's not what for..in does, and misunderstanding this will bite you. :-) It breaks as soon as you add any further properties to the array (a perfectly valid thing to do) or any library you're using decides to extend the array prototype (also a valid thing to do).
In any case, what you get back from document.getElementsByTagName isn't an array. It's a NodeList. Your best bet for iterating through NodeLists is to use an explicit index a'la Pointy's answer -- e.g., a straight counting loop:
var i;
for (i = 0; i < list.length; ++i) {
// ... do your thing ...
}
Somewhat off-topic because it doesn't relate to your NodeList, but: When you are actually working with a real array, because arrays in JavaScript are sparse, there is applicability for for..in, you just have to be clear about what you're doing (looping through property names, not indexes). You might want to loop only as many times as the array has actual entries, rather than looping through all the indexes in the gaps in the sparse array. Here's how you do that:
var a, name;
a = [];
a[0] = "zero";
a[10000] = "ten thousand";
for (name in a) {
// Only process this property name if it's a property of the
// instance itself (not its prototype), and if the name survives
// transition to and from a string unchanged -- e.g., it's numeric
if (a.hasOwnProperty(name) && parseInt(name) == name) {
alert(a[name]);
}
}
The above only alerts twice, "zero" and "ten thousand"; whereas a straight counting loop without the checks would alert 10,001 times (mostly saying "undefined" because it's looping through the gap).
The problem with the for ... in construct is that everything from the prototype(s) gets included in the enumeration.
What your output is showing is basically every attribute of images, which is an array object. So you get
all the elements in your array, i.e. the numbers that appear in your output.
all the properties available on your array. This is why you see length in your output for example.
Your code breaks due to number 2, since a functions does not have a style attribute
So, yes, as Pointy shows, the correct way to iterate over the elements of an array in JavaScript is by using a for loop.
Once the page has been loaded, I would like to append an additional element for each existing elements on the page.
I tried something like this:
var divs=document.getElementsByTagName('div');
for(i=0;i<divs.length;i++){
newDiv=document.createElement('div');
divs[i].appendChild(newDiv);
}
Just a warning this will actually freezes the browser because the divs variable is dynamic and divs.length just gets larger and larger each time the loop goes.
Is there a way to determine the number of tags when the DOM is normally loaded for the first time and have a chance to work with the elements statically.
I can't there of another solution so far.
Thanks so much.
Dennis!
The problem is that DOM collections are live, and when the underlying document structure is changed, it will be reflected automatically on the collection, that's why when the length property is accessed it will contain a new length, a common approach is to cache the length before starting the loop:
var divs=document.getElementsByTagName('div');
for(var i = 0, len = divs.length;i<len;i++){
var newDiv = document.createElement('div');
divs[i].appendChild(newDiv);
}
Also notice that you should declare all your variables with the var statement, otherwise it might become global.
Edit: In this case, since you are appending child nodes of the same tagName, the collection will be modified, and the indexes will no longer match, after the first iteration, the index 1 will refer to the newDiv object from the previous iteration, as #Casey recommends it will be safer to convert the collection to a plain array before traversing it.
I use the following function:
function toArray(obj) {
var array = [];
// iterate backwards ensuring that length is an UInt32
for (var i = obj.length >>> 0; i--;) {
array[i] = obj[i];
}
return array;
}
//...
var divs = toArray(document.getElementsByTagName('div'));
//...
Like you said, the divs variable is dynamic, so you have to convert it into an array (which is static) before you use it.
var nodeList = document.getElementsByTagName('div');
var divs = [];
for (var i = 0; i < nodeList.length; i++)
divs.push(nodeList[i]);
// loop again and append the other divs
Another (more elegant) way to do this is:
var divs = Array.prototype.slice.call(document.getElementsByTagName('div'));
But alas, this method does not work in IE.
Using jQuery, this is pretty straight forward. You can get a reference to all the existing divs or any other element on the page and then append a new element very easily without needing to create an explicit loop. Hope this help.
$('div').each(function(){
var newDiv = document.createElement('div');
$(this).append(newDiv);
});
document.getElementsByTagName() does NOT return a plain array, but an instance of HtmlCollection, which behaves like an array, but in fact presents some kind of view to all elements with the given element name in the document.
So, whenever you insert something into the DOM, the length property of divs will be updated too - of course.
So, besides other answers here, this behaviour should make sense now ;-)
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();
}