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
Related
I'm getting two extra/unexpected array entries that are undefined. These show up when I iterate over an array of element nodes with 3 entries and push the values into another array:
I have a select element with 4 options in it. I want to put the last 3 options.innerText into an array
<select>
<option>skip me</option>
<option>text1</option>
<option>text2</option>
<option>text3</option>
<select>
I made a variable and grabbed the nodes:
var options = document.querySelectorAll('select option:not(:first-child)')
this gave me an array with 3 option elements in it as confirmed in the console
>>options
<<[<option>text1</option>,
<option>text2</option>,
<option>text3</option>]
options.length is 3.
iterating over the array proceeds as expected, but also includes a function in the log?
>>for (i in options){console.log(options[i])}
<< <option>text1</option>
<option>text2</option>
<option>text3</option>
<< 3
<< function item() {[native code]}
When I iterate over the array and push the innerText into a new array, I get not 3, but 5 entries in the array
>>var texts = [];
>>for (i in options){texts.push(options[i].innerText)}
This gives me an array texts with 5 values: ['text1','text2','text3',undefined,undefined]
texts.length is 5
Why am I getting those two extra array entries?
I'm sure this is something elementary that I just haven't come across yet, can anyone explain?
Try iterating through a general for loop.
for (var i = 0; i < options.length; i++) {
texts.push(options[i].innerText)
}
http://jsfiddle.net/d02urj1n/
Because for (i in options) iterates through the properties of the options object. for/in was made to iterate over enumerable properties.
Array indexes are just enumerable properties with integer names and
are otherwise identical to general Object properties. There is no
guarantee that for...in will return the indexes in any particular
order and it will return all enumerable properties, including those
with non–integer names and those that are inherited.
So, while you may find some iterations of forEach or you can roll out your own, it is safer to loop over an array using a normal for loop.
If you are using jquery use $.map() like so...
var opts = $("select > option:not(:first-child)");
var texts = [];
$.map(opts, function(val, key){
texts.push(val.innerText);
});
I suggest to use basic JavaScript For Loop, just like following :
for (var i=0 ; i < options.length ; i++){
console.log(options[i]);
}
//That will give you the expected result
<option>text1</option>
<option>text2</option>
<option>text3</option>
NOTE : Please take a look at Why is using “for…in” with array iteration such a bad idea?.
Hope this helps.
Looping through the properties in the object gives you the following items:
"0": <option>text1</option>
"1": <option>text2</option>
"2": <option>text3</option>
"length": 3
"item": function () {[native code]}
The reason that you get a length and item property also, is that the object is not an array, it's a NodeList object. It works as an array for methods that expect an array because it has a length property and numbered items, so it's what's called an array-like object. The for( in ) loop doesn't expect an array.
Loop through the items as if it was an array, using the length property:
var texts = [];
for (var i = 0; i < options.length; i++){
texts.push(options[i].innerText);
}
Thanks everyone, after some more inspection I did find some injected code objects! The standard for loop worked as expected. I didn't realize that for... in... would bring in inherited properties. Learn something new everyday.
I have a some dom objects that are selected with :
var elems = document.getElementsByClassName("royal") ;
and also another objects
var collapsedElems = document.getElementsByClassName("collapsed");
my problem occured when i tried to concat elems and collapsedElems with array concat() method
elems.concat(collapsedElems)
but the return type of getElementsByClassName() is not array actually it is
object. I checked it at chrome console with typeof operator. that seems weird to me how can i combine this two group of object. ?
getElementsByClassName() returns an HTMLcollection object which is similar to an array but not really an array so you can't call array methods using the returned value.
One hack is to use Array's prorotype methods along with .call()/.apply() to pass the returned object as the context.
var elems = document.getElementsByClassName("royal") ;
var collapsedElems = document.getElementsByClassName("collapsed");
var earray = Array.prototype.slice.call(elems, 0);
var concatenated = earray.concat.apply(earray, collapsedElems) ;
console.log(concatenated)
Demo: Fiddle
It returns an HTML Collection which does things that arrays do not (such as getting live updates when the DOM changes).
If you want to get an array with elements from both collections in it, then you could:
Create an array and then populate it by looping over each of the collections and pushing each member into it (which will give you an array) or
Use document.querySelectorAll(".royal, .collapsed"); (gets a NodeList)
From the MDN:
Returns an array-like object of all child elements which have all of
the given class names. When called on the document object, the complete
document is searched, including the root node. You may also call
getElementsByClassName() on any element; it will return only elements
which are descendants of the specified root element with the given
class names.
You can try this:
function getElementsByClassName(className)
{
if (document.getElementsByClassName)
{
return document.getElementsByClassName(className);
}
else
{
return document.querySelectorAll('.' + className);
}
}
I just want to change a classname to another one.
I've tried using
document.getElementsByClassName("current").setAttribute("class", "none");
but it doesn't work. I'm new at javascript.
Explanation
document.getElementsByClassName returns an HTMLCollection, not just an array of elements. That means that the collection is live, so in this specific situation, it retains the requirement that it will always hold all elements with the class "current".
Coincidentally, you are removing the very class that the collection depends on, therefore updating the collection. It would be totally different if you were setting the value property (for example) in the loop - the collection wouldn't be affected, because the class "current" wasn't removed. It would also be totally different if you were adding a class, such as el.className += " none";, but that isn't the case.
A great description from the MDN docs:
HTMLCollections in the HTML DOM are live; they are automatically updated when the underlying document is changed.
Approach 1
An easy way to overcome all this pandemonium is by looping backwards.
var els = document.getElementsByClassName('current'),
i = els.length;
while (i--) {
els[i].className = 'none';
}
DEMO: http://jsfiddle.net/fAJgT/
(the code in the demo has a setTimeout, simply so you can see the original border color at first, then after 1.5 seconds, see it change)
This works because it modifies the last item in the collection - when it is modified (and automatically removed), move onto the item before it. So it doesn't suffer any consequences of the automatic removal.
An alternate setup, doing the same thing, is:
for (i = els.length; i >= 0; i--) {
Approach 2
Another answer made me realize you could just continually operate on the first item found. When you remove the specific class, the element is removed from the collection, so you're guaranteed that the first item is always a fresh item in the collection. Therefore, checking the length property should be a safe condition to check. Here's an example:
var els = document.getElementsByClassName('current');
while (els.length) {
els[0].className = 'none';
}
DEMO: http://jsfiddle.net/EJLXe/
This is basically saying "while there's still items in the collection, modify the first one (which will be removed after modified)". I really wouldn't recommend ever using that method though, because it only works specifically because you end up modifying the collection. This would infinitely loop if you were not removing the specific class, or with a normal array or a non-live collection (without spliceing).
Approach 3
Another option is to slice (shallow copy) the collection into an array and loop through that normally. But I don't see any reason/improvement to do that. Here's an example anyways:
var els = document.getElementsByClassName('current'),
sliced = Array.prototype.slice.call(els), i;
for (i = 0; i < sliced.length; i++) {
sliced[i].className = 'none';
}
DEMO: http://jsfiddle.net/LHe95/2/
Approach 4
Finally, you could use document.querySelector - it returns a non-live NodeList (therefore you can loop like normal), and even has better support in browsers than document.getElementsByClassName does. Here's an example:
var els = document.querySelectorAll('.current'),
i;
for (i = 0; i < els.length; i++) {
els[i].className = 'none';
}
DEMO: http://jsfiddle.net/xq8Xr/
References:
document.getElementsByClassName: https://developer.mozilla.org/en-US/docs/DOM/document.getElementsByClassName
HTMLCollection: https://developer.mozilla.org/en-US/docs/DOM/HTMLCollection
slice: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/slice
querySelectorAll: https://developer.mozilla.org/en-US/docs/DOM/Document.querySelectorAll
document.getElementsByClassName("current") returns a HTMLCollection, not an HTMLElement. You can access elements in the list the same way you would access an array.
The following will change the class of the first element in the HTMLCollection.
document.getElementsByClassName("current")[0].setAttribute("class", "none");
However, you do not have to use the setAttribute method, you can just set the className property of the element.
element.className = 'none';
Please note that HTMLCollection are live, wich means that as soon as the element will not have the current class name anymore, the element will be removed from the array, so to avoid any issues you could iterate over it using the following approach:
var list = document.getElementsByClassName("current");
while (list.length) {
list[0].className = 'none';
}
I have to push elements in an associative array from another array after some processing and I'm doing something this:
for(var i in this.aNames) {
var a = this.aNames[i];
// some processing on a
aList[i] = a;
aList.push(i);
}
But it's not giving me the proper array.
EDIT :
Here aNames is an associative array like this
'1232':'asdasdasd',
'6578':'dasdasdas'
...... and so on of about 100 elements.
I'm using for here as I want to do some changes in every element of the array.
Then I'm displaying the result array on the page but it's showing the key value together with the array data.
I.e. it should only display asdasdasd or asdasdasd but it's displaying keys too, like 1232 asdasdasd 6578 dasdasdas.
There are multiple things which may go wrong...
Primarily, make sure that this is pointing to the correct context and that this.aNames is actually returning a complex object (associative array).
Also, what's aList? Is it an array? If it is, push should append your array with the key of the current member (the member's name).
If you want to append the values of the members on your source object, you need to do something like this:
var obj = {name: 'dreas'},
arr = []; // arr is an array
arr.push(obj["name"]); // arr now contains a single element, 'dreas'
In your for..in construct, you are both adding elements to an alleged array (aList) with push but also creating new members on your array (with the subscript notation, aList[i] = "asd" since i in this case (for..in iteration) refers to the member's name).
So, what you need to do is decide if you want to add elements to an array or members to an object, not both.
If you just want to clone an array, use a for loop. If on the other hand you want to clone an object, it's not that trivial because members can also be complex objects containing their own members, and simply doing arr[i] = obj.member will only copy a pointer to arr[i] if member is a complext object, not a value type.
Just to make sure my terminology is understandable:
var anObject = {name: "dreas"},
anArray = [1,2,3];
anObject["name"] <= member (results in "dreas")
anArray[1] <= element (results in 2)
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.