How can I get child elements by element name with JavaScript? - javascript

I have an XML file with many nodes that look just like this:
<map-item>
<location-name>U Lot (Pine Sreet)</location-name>
<link>https://maps.google.com?daddr=Pine+Avenue+Middletown+CT+06457</link>
<latitude>41.5501724</latitude>
<longitude>-72.6588056</longitude>
<icon-path>img/parking-icon.svg</icon-path>
</map-item>
I've successfully looped through all the of the map-item elements in the XML file like this:
markers = xml.documentElement.getElementsByTagName("map-item");
for (var i = 0; i < markers.length; i++) {
//do something here for each marker
}
But I'm having a hard time figuring out how to drill down and get at the child elements inside of each map-item element, such as location-name or icon-path. What do I need to do in order to grab the values of those child elements and do something with them?

You can use getElementsByTagName again, on each marker. If the xml is valid and conforms to some schema, you can expect that there is at least one element of the given name in your <marker>.
var markers = xml.documentElement.getElementsByTagName("map-item");
for (var i = 0; i < markers.length; i++) {
var marker = markers[i];
var locationName = marker.getElementsByTagName("location-name")[0].textContent;
var iconPath = marker.getElementsByTagName("icon-path")[0].textContent;
…
}
If you don't know how many child elements will be there in each <marker>, you'll have to switch on the .length of the selected list or loop again.

Have you tried with markers[0].childNodes?
for (var i = 0; i < markers[0].childNodes.length; i++) {
// NOTE: List is live, Adding or removing children will change the list
markers[0].childNodes[i];
}
See Mozilla Documentation
#Prasanth has been kind enough to demonstrate with a fiddle to demonstrate (I modified the output so it wouldn't be an alert).

Related

For loop within a for loop? - Javascript

newbie javascript question. I made sure to research as much as I could before posting here, I've tried many solutions but could be searching for the wrong thing.
I've attached an image below of the issue I have. I'm trying to retrieve everything in the dark blue boxes, but I can't identify those input tags as there is nothing unique about them, I can however identify their parent divs by the class 'f-active'. When the divs have that class they have been selected by the user which is what I am interested in.
My attempt so far
var divArray = document.querySelectorAll('div.add-filter.f-active');
var arr = [];
for(var i=0; i < divArray.length; i++){
var childArray = divArray[i].children;
// console.log(childArray);
for(var i=0; i < childArray.length; i++){
if(childArray[i].tagName == "INPUT"){
var catNameCollection = arr.push(childArray[i].name);
// console.log(catNameCollection);
}
}
}
I tried to use a for loop to get all the parents, then use another for loop to select the children (input tags) and then grab the name attribute, however it is just outputing numbers. I did originally try to create 'divArray' as document.querySelectorAll('div.add-filter.f-active').children, but this and then grab the name attribute in the for loop, but this didn't return anything at all.
Any help anyone could offer would be greatly appreciated, I'd love to know what I'm doing wrong.
Thank you!
Your i is same for both loops. Use:
var divArray = document.querySelectorAll('div.add-filter.f-active');
var arr = [];
for(var i=0; i < divArray.length; i++){
var childArray = divArray[i].children;
// console.log(childArray);
for(var k=0; k < childArray.length; k++){
if(childArray[k].tagName == "INPUT"){
var catNameCollection = arr.push(childArray[k].name);
// console.log(catNameCollection);
}
}
}
Classic for-loops usually aren't the best tool for iterating through DOM elements - they add a lot of clutter and are error-prone, especially when you have to nest them.
In your case it'd be simpler to instead modify your query to directly grab all input elements with a div.f-active parent, then extract the names by iterating through them with a forEach. For example (using ES6 or higher):
const arr = [];
// Get list of all <input> elements that have <div> element parents with class f-active.
const nodes = document.querySelectorAll('div.add-filter.f-active > input');
// Extract name from each input element matched by your selector.
nodes.forEach(node => arr.push(node.name));
Or if you're stuck using ES5:
var arr = [];
var nodes = document.querySelectorAll('div.add-filter.f-active > input');
nodes.forEach(function(node) {
arr.push(node.name);
});
Here's a quick JSFiddle I put together to demonstrate the concept for you. (You'll need to open the console to see the result)
Hopefully that helps :)

obtaining all items of listView in winjs

I have a listview div on screen and I have added itemDataSource to it successfully
var lettersList = new WinJS.Binding.List(jsonArrayForClearance);
var list_ = document.getElementById("prodListView").winControl;
list_.itemDataSource = lettersList.dataSource;
list_.itemTemplate = document.getElementById("tileTemplate");
list_.forceLayout();
Now in each item I have added a custom input type for user to specify(using template). I want to iterate through all the items of list and obtain the value of that input type in an array.
how can I do it?
EDIT: My question is to access custom input type declared in list items. As such we use following code to access any input type named "inpT"
document.getElementById('inpT');
but how to access the same from list item? can I use Following code(as suggested by user2608614)
var listView = document.getElementById("prodListView").winControl;
var list = listView.itemDataSource.list;
for (var i = 0; i < list.length; i++) {
var item = list.getAt(i);
item.getElementById('inpT');
}
You can iterate through the list elements like this:
var listView = document.getElementById("prodListView").winControl;
listView.itemDataSource.getCount()
.done(function(count) {
for (var i = 0; i < count ; i++) {
listView.itemDataSource.itemFromIndex(i)
.done(function (item) {
//***item will contain your property
});
}
});
Is not the best solution as it make it difficult to chain the promises, I'm also looking for a better one. But it works.
Since you're using a Binding.List you can just iterate through that much like an array.
var listView = document.getElementById("prodListView").winControl;
var list = listView.itemDataSource.list;
for (var i = 0; i < list.length; i++) {
var item = list.getAt(i);
// do something with this item
}
The only thing to remember is that it doesn't support [] and instead you have to use .getAt() and .setAt().

JS difficulty to document.getElements and output HTML

I'm trying to output some existing HTML code later in the HTML document. I can't change the HTML code to output and only have JS to do so.
The relevant code is a unordered list with a class used for other tags:
<ul class="styling">...
The JS code
var AllUl=document.getElementsByTagName("ul");
var AdminUL=AllUl.getElementsByClassName("styling");
var AdminHTML=AdminUL.outerHTML;
Everything should be visible and editable here http://jsfiddle.net/fGF7g/3/
Sorry if I have obvious errors, I'm a JS beginner.
getElementsByTagName returns a list of elements with the given tag name, so you should do like:
var AllUl=document.getElementsByTagName("ul");
for(var i = 0; i < AllUl.length; i++ ) {
var AdminUL=AllUl[i].getElementsByClassName("styling");
}
The problem is that document.getElementsByTagName and AllUL.getElementsByclassName return arrays. To fix the problem, you will have to use a for loop to iterate through the elements with that class name, like this
var AllUL=document.getElementsByTagName("ul");
for(var i = 0; i < AllUL.length; ++i) {
var AdminUL=AllUL[i].getElementsByClassName("styling");
}

How do I apply different changes to each control when selecting by class?

I've got a series of checkboxes with the same class. I want to get all the checkboxes with that same class as a collection and extract their id and checked status. But all the select by class examples I've seen just apply a single change to all elements of that class. I need to get a collection/array of all the checkbox elements with that class so I can iterate over them.
Ok, I've tried some of the suggestions and had no luck.
This throws an Object Expected error in IE8:
var checkboxes = document.querySelectorAll("input[type='checkbox'].chkRoles");
And this gives me an empty array every time, regardless of what's checked.
var mandatoryRoleIDs = [];
$('input.chkRoles[type="checkbox"]').each(function (i, checkbox)
{
if ($(checkbox).attr('checked'))
mandatoryRoleIDs.push($(checkbox).attr('id'));
});
Since your question is tagged with jQuery, here's how you can do it with jQuery's each() function:
$('input.someClass[type="checkbox"]').each(function(i,el){
var $this = $(el),
id = $this.attr('id'),
checked = $this.attr('checked');
/* do stuff with id and checked */
});
var checkboxes = document.getElementsByClassName('classname');
Then iterate over it to do what you need.
for(var i = 0; i < checkboxes.length; i++) {
var current = checkboxes[i];
// stuff on current
}
Using jQuery is not necessary in this simple case, and querySelectorAll is not totally supported by older browser; Vanilla JS is the best way to do it!
Since you need to support IE8(damn Microsoft) you should do something like this:
var inputs = document.getElementsByTagName('input'),
className = "classname";
for (var i = 0; i < inputs.length; i++) {
var current = inputs[i];
if (current.type !== 'checkbox' || current.className.indexOf(className) === -1) continue;
//do stuff with current
}
See a working JSFiddle example of this snippet.

Optimization appending <li> into <ul> with jQuery

I populate a list with search results appending li elements. I update DOM for each result.
for (var i = 0; i < topics.length; i++) {
$("#searchResults").append(
$("<li />")
.append(result.Name)
.addClass("example")
);
};
I want to make a group of li elements first and update DOM-tree just once.
I try something like this:
var list = $([]);
for (var i = 0; i < topics.length; i++) {
list.append(
$("<li />")
.append(result.Name)
.addClass("example")
);
};
$("#searchResults").append(list);
But div $("#searchResults") is empty.
Where is the problem?
Something like this should be much faster:
var ul = $("<ul />");
for (var i = 0, l=topics.length; i < l; i++) {
$("<li />", { text: result.Name, "class": "example" }).appendTo(ul);
};
$("#searchResults").append(ul.contents());
By using a document fragment ($("<ul />")) and appending to it, then appending at the end, we're not messing with the entire DOM each append. Also we're not repeatedly selecting #searchResults each loop...or checking .length would could also be expensive.
Note: this method still uses the DOM to create elements (as opposed to a string), eliminating issues of result.Name having HTML that could screw things up, etc.
Creating DOM elements on the fly will usually be slower than just using innerHTML (or a wrapper around that). Also, concatenating string with + will usually be slower than using Array.join('').
In the end, I suspect something like this would be the fastest:
var list = [];
for (var i = 0; i < topics.length; i++)
list.push("<li class=example>",topics[i].Name,"</li>");
$("#searchResults").html(list.join(''));
Try just using a string. Add all your li's to a string and then put them into the innerHTML of the searchResults div.
var list = '';
for (var i = 0; i < topics.length; i++) {
list +="<li class=example>" + result.Name + "</li>";
}
$("#searchResults").innerHTML = list;
If you are looking for efficacy this is probably better because you are not using the DOM engines a lot. (although unless you are adding hundreds of li's it is probably insignificant anyway.
All previous solutions still suffer from recalculating, painting and layout for every single element we add.
Instead of appending the elements directly to the document when they are created, append them to the DocumentFragment instead, and finish by adding that to the DOM.
var el;
var i = 0;
var fragment = document.createDocumentFragment();
while (i < 500) {
el = document.createElement('li');
el.innerText = '1ist ' + i;
fragment.appendChild(el);
i++;
}
div.appendChild(fragment);

Categories