I have three <div> elements with the same class of "child". Imagine that I cannot add any other class to the <div>. How can I find only two of those divelements (second and third) with JavaScript? (the code below is just for better understanding of what I mean - mind that <div> elements with the same class are not siblings).
<div class="parent">
<div class="child"></div>
<div class="child">
<div class="new">
<div class="child"></div>
</div>
</div>
</div>
The DOM defines a getElementsByClassName function, which returns a HTMLCollection object:
var elementsWithChildClass = document.getElementsByClassName("child");
In your case, this will return the 3 div elements with the "child" className. You can then access a given element on this list using the bracket notation syntax, for example:
var secondElem = elementsWithChildClass[1];
var thirdElem = elementsWithChildClass[2];
In your case, both of these objects will be HTMLDivElement instances (which inherits from HTMLElement).
Note: the index of the HTMLCollection object is zero-based, which means the first element is accessed using [0], second element using [1], and so on.
var slice = function(elements, start, end) {
var sliced = Array.prototype.slice.call(elements, start, end);
return sliced;
};
var totalChildElem = document.getElementsByClassName("child");
if (totalChildElem.length)
{
var myChildElem = slice(totalChildElem,1);
}
Revised Question: How can I to get the last two elements with class="child", not looking at the level they are nested (second and third) with JavaScript?
You do not need jQuery to do this.
An answer that works all the way down to at least ES3 might look like this. In this case, supplying a parentId allows you to target groups of three .child classes, instead of making JavaScript collect all .child classes.
function get2ndAnd3rdClassNodes(parentId, targetClass)
{
var targets = [];
var nodes = document.getElementById(parentId).getElementsByClassName(targetClass);
for(var i = 1, length = nodes.length; i < length; ++i)
{
target[i - 1] = nodes[i];
}
nodes = null;
return targets;
}
A more modern answer might look like this:
function get2ndAnd3rdClassNodes(parentId, targetClass)
{
var nodes = document.getElementById(parentId).getElementsByClassName(targetClass);
return Array.prototype.splice.call(nodes, 1, 2);
}
Sometimes it is advantageous to separate DOM access from the code that manipulates the values returned from the DOM. In that case, the function could work with nodes, instead of starting with an id (and target classname). .getElementsByClassName() returns a nodeList, which is an array-like object, but an object nonetheless. In order to use JavaScript array methods with it, you need to, in effect, cast it to an array.
function get2ndAnd3rdClassNodes(nodes, targetClass)
{
nodes = nodes.getElementsByClassName(targetClass);
return Array.prototype.splice.call(nodes, 1, 2);
}
Or, in one line:
function get2ndAnd3rdClassNodes(nodes, targetClass)
{
return Array.prototype.splice.call(nodes.getElementsByClassName(targetClass), 1, 2);
}
Finally, if you want to be able to target how many elements to get rid of in a flexible way:
function spliceClassNodes(nodes, targetClass, startIndex, nodesNeeded)
{
var minNodes = startIndex + nodesNeeded;
try
{
if(nodes.length < minNodes)
{
throw new RangeError('Only ' + nodes.length + ' received. Needed ' + minNodes + ' to process this nodeList correctly.');
}
return Array.prototype.splice.call(nodes.getElementsByClassName(targetClass), startIndex, nodesNeeded);
}
catch(e)
{
if(e instanceof RangeError)
{
//Handle the problem.
}
}
return;
}
Invoked as:
var childArray = spliceClassNodes(nodes, 'child', 1, 2);
Feel free to add this to a prototype.
<script>
document
.getElementById('country')
.addEventListener('change', function() {
'use strict';
var value1 = this.value;
console.log(value1);
var vis = document.querySelectorAll('.input-group-addon'),
country = document.getElementsByClassName(value1);
console.log(country.length);
// Point One
var i;
if (vis !== null) {
for (i = 0; i < vis.length; i++)
vis[i].className = 'input-group-addon inv';
console.log(country.length);
// Point Two
}
if (country !== null) {
for (i = 0; i < country.length; i++) {
country[i].className = 'input-group-addon';
// Point Three
}
}
});
</script>
This has been bothering me for a while now. I am trying to get the value of a selected value in
document.querySelectorAll('.input-group-addon')
and find matching class names in
document.getElementsByClassName(value1)
The nodelist of country is available at Point One and changes to null at Point Two.
Is there a basic logic or syntax error in my code?
and changes to null at Point Two
I assume you mean that the list is empty. The variable should not magically become null.
getElementsByClassName returns a live HTMLCollection. Meaning it will always reflect the current state of document. If you change the class name of an element, it will automatically either be added or removed from the collection.
If you don't want that, then either use querySelectorAll, which returns a collection that is not live, or convert the collection to an array.
i am trying to create funcion for creating decision tree with state of game in every node in game (doesnt matter what game). I wrote recursive function (DFS) like this:
function makeTree(anchor,count,player){
var subTree=null;
var nodes=[];
if(player)var newPlayer=false;
else var newPlayer=true;
for (var i = 0; i <= 9; i++) {
for (var j = 0; j <= 9; j++) {
if(anchor["state"][i][j]==0){
var newState=anchor["state"];
if(player)newState[i][j]=1;
else newState[i][j]=2;
var node={name:i+"_"+j, contents:[],state:newState, value:null, player:newPlayer};
if(count>0){
var newCount=count-1;
subTree=makeTree(node,newCount,newPlayer);
node["contents"]=subTree;
}
nodes.push(node);
}else{
continue;
}
}
}
return nodes;
}
And with call:
var tree={};
var hrac=true;
var plocha=[[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0]];
var state=plocha;
tree={name: "root",
contents:[],
state:state,
value:null,
player: hrac};
tree["contents"]=makeTree(tree,3,hrac);
But the function change variables in different scope, so the output tree will be like this:
"root" - node - node - node
- node
- node
- node
- node
I cant figure out what is going on with variable newState in that function, because after finish the recursion the original variable plocha has the value of the latest node["state"]. Any suggestions what to do?
EDIT: Thanks to Bergi i realize that i need to do deep copy of array insted of make reference to it, so i make funcion for copy of array and now this works. Thank you Bergi!
Your state property is an array, which is mutable. On every assignment, you change the one multidimensional array that is the state of all nodes. You'll want to make every newState a new array, instead of passing your plocha reference recursively through all functions:
…
var newState = anchor["state"].slice(); // create copy of the outer array
newState[i] = newState[i].slice(); // copy of the row to be modified
newState[i][j] = player ? 1 : 2;
…
My xml file is like:
it contains different 'object' nodes and in different objects there are different parameters one is deleted parameter.
I want to delete the all 'object' nodes that contains the deleted parameter 1.
This is the code that deletes the node object which has a parameter node deleted =1:
x=xmlDoc.documentElement;
for(var count=0; count<5;count++){
var y=x.getElementsByTagName("deleted")[count]; //Find that nodes arent
if(y.textContent == "1") {
var z=y.parentNode; //delete the node from the parent.
x.removeChild(z);
Xml2String1= new XMLSerializer().serializeToString(x);
}
}
Your loop is incorrect:
for(var x1=0; x1<5;x1++){
var y=x.getElementsByTagName("deleted")[x1];
Your loop runs for 5 iterations without regard for the number of <deleted> elements are found. Each time through the loop you search again and get a new NodeList/HTMLCollection of the remaining <deleted> elements, but your loop counter is incremented regardless.
Try this instead:
var deletedNodesList = x.getElementsByTagName("deleted");
var nodesToDelete = [];
for (var index = 0; index < deletedNodes.length ; index += 1)
{
var node = deletedNodes[index];
if (node.textContent == "1")
{
nodesToDelete.push( node.parentNode ); //delete the node from the parent
}
}
nodesToDelete.forEach( function() { x.removeChild(this); } );
Note that, per the documentation on MDN, the NodeList is a live collection, so don't modify it while you are processing it.
PS.
I second raam86's recommendation to use sane (meaningful) variable names. Meaningful variable names make it easier to understand the code, which makes it easier to write correct code and to resolve problems in incorrect code.
I have an array of objects. One of the properties of these objects is a jQuery reference to a DOM element that may or may not actually be attached to the DOM at any given time;
For example:
Array = [{
name : 'whatever 1',
element : $('<div id="item_1" class="item"><img src="" /></div>')
},
{
name : 'whatever 2',
element : $('<div id="item_2" class="item"><img src="" /></div>')
}];
When this array is untouched I can detach and append these elements to the DOM without any troubles as well as use standard jQuery methods upon the elements.
For example:
Array[0].element.find('img');
...Will work fine.
However if I sort or splice this array, I lose the references.
I understand the reason why this is happening but what I would like to know is if there is anyway around this so that this element can continually be changed, attached, detached, modified while sorting or splicing the overall array itself?
Thanks in advance.
EDIT:
Here is a code sample of my rearrange function:
rearrangeItems : function(){
var self = this;
var offset = 0;
// get number of items that are less than insertindex
for(var i = 0; i < self.cache.selecteditems.length; i++) {
if(self.cache.selecteditems[i] < self.cache.rearrangepos){
offset++;
}
}
//subtract the offset from the intended insertion index
var rearrangeindex = self.cache.rearrangepos - offset;
var removedItems = [];
//sort the selected element indexes into ascending order
self.cache.selecteditems.sort(function (a, b) {
if (a < b) return -1;
else if (b < a) return 1;
return 0;
});
//remove the selected array elemens from the overall array and push them into the temporary array
for(var i = 0; i < self.cache.selecteditems.length; i++) {
var index = self.cache.selecteditems[i];
removedItems.push(self.cache.items.splice(index - removedItems.length, 1)[0]);
}
//Add the selected array elements back into the main array at the correct insertion point
self.cache.items.splice.apply(self.cache.items, [rearrangeindex, 0].concat(removedItems));
}
When calling this function all array elements are reordered exactly as intended.
Before reordering I can do the following:
self.cache.items[index].html.find('img');
Afterwards however, it will result in an empty object (the html property is the equivalent of the element property in my example above).
I would work with the ID, cause you have one. Don't know if this is the cleanest solution but it will work.
Calling your example like this:
$('#' + Array[0].element.attr('id')).find('img');
Hope this works for you.
Sadly this was down to my own stupidity. In my code I was referencing the element incorrectly.
I was actually doing the following:
self.cache.items[index].html.find('#image_' + index);
After reordering the elements I was intentionally resetting indexes afterwards, therefore when calling this after a sort/reorder the element was incorrect.
by switching to a class selector everything was fixed.
self.cache.items[index].html.find('.image_item');
How embarrassing! My apologies to all.