This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
adding 'click' event listeners in loop [duplicate]
(5 answers)
Closed 7 years ago.
For my project, I am required to get the collection of all the elements which have the same class. So, I am doing it with getElementsByClassName.
var tabs = document.getElementsByClassName("tab");
if (tabs) {
for (var i = 0; i < tabs.length; i++) {
var tab = tabs[i];
console.log(tab); // which outputs as 'undefined'..
console.log(i); // which outputs the nos of the total nodes available with class 'tab'
}
}
I have to retrieve all those elements(which has class 'tab') and apply it another class (say 'bat')..
My question is, why it consoles out undefined as above?? and how can I be able to iterate through all the required nodes sequentially. Is there any code available which can do this??
EDIT ::
Forget to add it..
When clicked on any tab which has 'tabs' class, it always returns the same result..
var tabs = document.getElementsByClassName("tab");
if (tabs) {
for (var i = 0; i < tabs.length; i++) {
var tab = tabs[i];
tab.addEventListener("click", function() {
console.log(tab[i]); // always returns undefined
console.log(tab); // always returns the last element in the list
console.log(i); // always returns the length of that list (in my case its 3)
}, false);
}
}
How can I be able to iterate through all the available elements sequentially.
You can change a NodeLiist into an array. Btw, your tab[i] should be tabs[i] I think.
var tabs = document.getElementsByClassName('tab');
var tabArray = Array.prototype.slice.call(tabs);
for(var i = 0; i < tabArray.length; i++) {
console.log(tabArray[i].id);
console.log(i);
tabArray[i].innerHTML = "t"+(i+1);
};
<div id="t1" class="tab">t</div>
<div id="t2" class="tab">t</div>
<div id="t3" class="tab">t</div>
<div id="t4" class="tab">t</div>
<div id="t5" class="tab">t</div>
<div id="t6" class="tab">t</div>
<div id="t7" class="tab">t</div>
<div id="t8" class="tab">t</div>
Be careful with getElementsByClassName(), as said in mozilla docs the return is a array-like object not a array. Another point is that return is a live HTMLCollection so if you want to change the class name through a loop the collection will be live updated on the fly and will cause unexpected behaviors.
I made a example with class change case. Check complete code on link below.
http://codepen.io/renatocolaco/pen/VemMNy
function btnClick(event){
event.preventDefault();
//action goes here
var boxes = document.getElementsByClassName("box");
console.log("fouded elements: " + boxes.length);
var element = boxes.item(0);
while(element){
element.className = "another_box";
element = boxes.item(0);
}
//doesn't work
//var cache = boxes.length;
//for(var i = cache; i < boxes.length; i++){
// console.log(boxes[0]);
// boxes[0].className = "another_box";
// }
//doesn't work
//Array.prototype.forEach.call(boxes, function(element, index, array){
//array[index].className = "another_box";
//});
}
Update:
The second problem, when you are adding event to the tabs, you have a closure problem.
This is because, at the point that the onclick method is invoked (for any of the tabs), the for loop has already completed and the variable i already has a value(the size of your collection).
Wrap your code inside a self executed function like this:
(function (i) {
tab.addEventListener('click', function() { console.log(i); });
})(i);
Have you used JQUERY before? That might be an easier path. This way you can use $(".tab"). You can also use .forEach() instead of a for loop? Might make it more simple
Also, Paul's example seems to be working, can you provide a non working JSfiddle?
Related
This question already has answers here:
How to loop through all the elements returned from getElementsByTagName [duplicate]
(10 answers)
Closed 5 years ago.
I m storing all the elements from a getElementsByClassName to a variable, I would imagine I can foreach loop this variable to get every single ids from it. But its not working.
var el = document.getElementsByClassName("machine_btn_over_layer");
el.forEach(test);
var test = function() {
console.log("test");
}
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>
What am I doing wrong? Im getting error saying function is not working
You need to make two changes. First document.getElementsByClassName is a HTMLCollection so array method will not work on this. So to use the array method you can convert it to array using spread operator(...)
Secondly a function declared var test = function() {} is never hoisted. So when el.forEach is called it don't get the function, hence it will throw undefined is not a function
var el = [...document.getElementsByClassName("machine_btn_over_layer")];
var test = function() {
console.log("test");
}
el.forEach(test);
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>
Use the documentation to help you! document.getElementsByClassName:
var elements = document.getElementsByClassName(names)
elements is a live HTMLCollection of found elements.
The first thing you should notice on the HTMLCollection documentation is that it does not have a method named forEach. But it does have a property named length
HTMLCollection.length
Returns the number of items in the collection.
And a method named item
HTMLCollection.item()
Returns the specific node at the given zero-based index into the list. Returns null if the index is out of range.
So you should be able to do this:
for (var i = 0; i < el.length; i++) test(el.item(i));
Or use the array sugar:
for (var i = 0; i < el.length; i++) test(el[i]);
Not every browser implements Array.prototype methods on HTMLCollection returned by getElementsByClassName method.
Also variable assignments do not hoist. Function declarations do.
var el = document.getElementsByClassName("machine_btn_over_layer");
// manually call Array.prototype.forEach on HTMLCollection
Array.prototype.forEach.call(el, test);
// replace function expression with function declaration to hoist with value
function test() {
console.log("test");
}
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>
<div class="machine_btn_over_layer"></div>
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 7 years ago.
sorry for the title but I'm not sure what the correct terminology is for this issue. I am trying to dynamically generate a table of clickable elements using for loops in JavaScript. Each of the elements, when clicked, should trigger the same function but with different parameters. In my code I am setting the onClick function up like so:
elementArray[i].onClick = function() { clickFunction(i) };
However, when I do this the clickFunction is just taking whatever value i is currently set to, not what it was when I set the onClick function, which is what I want. Any ideas?
As I got your question you want a table having clickable element calling to same function with diff param. [Let me know if it's not your question]
So taking i (index or row no.) would be param to that function.
HTML :
<table id='myTable'></table>
Javascript :
for(i = 9; i >= 0; i--) {
var table = document.getElementById("myTable");
// Create an empty <tr> element and add it to the 1st position of the table:
var row = table.insertRow(0);
// Insert new cells (<td> elements) at the 1st and 2nd position of the "new" <tr> element:
var cell = row.insertCell(0);
// Add some text to the new cells:
cell.innerHTML = "<input type='button' onclick='callIt("+i+")'";
}
function callIt(index) {
alert(index);
}
I am considering that you want button to click.
Hope it will help you.
more you can get on w3schools
Here's a version of #Bhojendra Nepal's "duplicate question" applied to your question. With this HTML:
<div class="element-0">0</div>
<div class="element-1">1</div>
<div class="element-2">2</div>
And this script:
var elementArray = [];
var creatFunc = function($this,i){
return function() {
console.log(i);
$this.html(i + 1);
if (i>1) i++;
};
};
for (var i = 0; i < 3; i++) {
elementArray[i] = $('.element-' + i);
elementArray[i].click( creatFunc( elementArray[i] , i) );
}
by creating the functions in a separate function, we've isolated the variable. I added "if (i>1) i++;" to show how the new function keeps its own variable and can increment it (keep clicking on the third div to see the number keep increasing).
here's a jsfiddle:
https://jsfiddle.net/mckinleymedia/1cksb8ky/
I hope this helps.
Oh, I added jQuery to this. I'm not positive how to get this working in vanilla javascript off hand.
I've ran into a small snag with my code - below is my code:
var actionsAllowed = $(packet).find('actionsAllowed');
This returns to me the following in the firebug console:
Object[actionsAllowed]
Clicking "actionsAllowed" takes me into the packet and to the correct section, where I see the two listed actions.
I can expand the object and eventually see the following:
Object[actions]
0
actions
remove
remove()
attributes
[]
baseURI
"http://localhost:9000/testget#"
childElementCount
2
childNodes
NodeList[ActionOne, ActionTwo]
0
ActionOne
1
ActionTwo
length
2
item
item()
iterator
iterator()
__proto__
NodeListPrototype { item=item(), iterator=iterator()}
So under the NodeList I see the correct actions.
The issue I am having is that I don't know how to get those actions out of there and listed or even just have them available as separate variables.
My attempt at getting then logging each child:
function getActionsAllowed() {
var children = actionsAllowed.childNodes;
for (var i = 0; i < children.length; i++) {
console.log(children);
}
}
Problem is, ".childNodes" keeps returning as "undefined".
Is there another, better way to do this? Or is this correct but I've made a mistake?
Thank you.
Kind Regards,
Gary Shergill
EDIT:
working code for just one result:
var currentState = $(packet).find('currentState').text();
var actionsBanned = $(packet).find('actionsBanned').text();
EDIT 2:
Updated code to:
$(packet).find('actionsAllowed').each(function () {
var children = this.childNodes;
for (var i = 0; i < children.length; i++) {
var action = children[i].nodeName
console.log(action);
}
});
This works =) It logs each action one by one, so it's working. Just a matter of choosing how to change the console.log() to something more useful (need to define each one seperately...).
Will create a new thread if I have trouble and link it from here.
(my related thread: Returning Arrays and ChildNodes)
You can always extract the DOM nodes from the jQuery object using toArray or get.
var actionsAllowed = $(packet).find('actionsAllowed').get();
But that's generally not necessary since the jQuery object itself implements an extensive API to manipulate the nodes.
E.g. looping over the nodes
$(packet).find('actionsAllowed').each(function () {
//looping over childnodes
$(this).children().each(function () {
console.log($(this).text());
});
});
If you just want to children of actionsAllowed nodes directly, you can also do:
$(packet).find('actionsAllowed > *').each(function () {
console.log($(this).text());
});
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
jQuery Looping and Attaching Click Events
(4 answers)
Closed 9 years ago.
I have function process_row that appends tags to html, and those tags are chained to a function upon clicked. (in this case, simply alert(i), its position in the result array).
But however, upon being clicked, the newly generated alerts the length of the entire result array. I have tried many, many changes to try and make it work, but it doesn't.
Strange thou, fab_div.attr("id", result_data[0]); works fine !! In Chrome inspect element the id tags are displayed as they are, but the click function points everything to the last element in the array.
for example, if I do, fab_div.click(function () { alert(result_data[0]) });, I get the name of the LAST element in the array, doesn't matter which element was clicked.
can anyone please explain to me... WHY??
I think it may have something to do with $("<div>") where JQuery thinks it's the same div that it's assigning to. Is there any way around this? The 's are generated dynamically and I would not want to let PHP do the echoing. Plus the content may be updated realtime.
Example dataset :
Smith_Jones#Smith#Jones#janet_Moore#Janet#Moore#Andrew_Wilson#Andrew#Wilson
After many, many changes, still not working:
function process_row(data){
result_array = data.split("#");
if(result_array.length > 0){
result_data =result_array[0].split("#");
for(i = 0; i < result_array.length; i++){
result_data =result_array[i].split("#");
var fab_text = result_data[1] + " " + result_data[2]
var fab_div = $("<div>");
fab_div.addClass('scroll_tap');
fab_div.attr("id", result_data[0]);
fab_div.append(fab_text)
// fab_div.click(function () { alert(i) });
// ^ not working, try appending list of id's to id_list
id_list.push(result_data[0])
$('#ls_admin').append(fab_div)
}
for(j = 0; j < id_list.length; j++){
$('#' + id_list[j]).click(function () { alert(j) })
}
}
}
Original Attempt:
function process_row(data){
result_array = data.split("#");
if(result_array.length > 0){
result_data =result_array[0].split("#");
for(i = 0; i < result_array.length; i++){
result_data =result_array[i].split("#");
var fab_text = result_data[1] + " " + result_data[2]
var fab_div = $("<div>").append(fab_text).click(function () { alert(i) });
fab_div.addClass('scroll_tap');
fab_div.attr("id", result_data[0]);
$('#ls_admin').append(fab_div)
}
}
}
If you must use an alert, then you can encapsulate the click handler in a self executing function and pass the index to it. Like,
(function (index) {
fab_div.click(function () {
alert(index);
});
})(i);
Although, this is not a clean way to do it. Otherwise, if you are looking to just manipulate the div element is any way, then adding any method directly will also work. Like,
fab_div.click(function () {
alert($(this).attr('id'));
});
You can refer a jsFiddle here
Wonky Solution, but it worked! Haha! Big thanks to Kevin B.
function process_row(data){
result_array = data.split("#");
if(result_array.length > 0){
result_data =result_array[0].split("#");
for(i = 0; i < result_array.length; i++){
result_data =result_array[i].split("#");
var fab_text = result_data[1] + " " + result_data[2]
var fab_div = $("<div>").append(fab_text);
fab_div.addClass('scroll_tap');
fab_div.attr("id", result_data[0]);
$('#ls_admin').append(fab_div)
}
$("#ls_admin").children(this).each(function( index ) {
$(this).append($(this).click(function () { alert($(this).text()) }));
});
}
}
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
If I create a whole lot of HTML elements using a loop, like
for (i= 1; i < 100; i++) {
var my_element = document.createElement ("td");
row.appendChild (my_element);
my_element.onclick = function () {my_function (i));
}
then when the element is clicked, the value of i passed to my_function is always 100, regardless of what number element is calling it. I have worked around this by using
my_element.id = "something"+i;
my_element.onclick = function (e) {my_function (e.target.id)};
(For Internet Explorer, the target needs to be srcElement, apparently.) I am curious to know whether there is any way to create the function without having to add the ID to the element like this.
The value of i changes with each iteration of the loop. You need a closure to capture the value of i:
(function(i) {
my_element.onclick = function () {my_function (i)};
}(i))
If you write a function which builds you a handler function, you can use the new scope which that gives you to ensure that you get the number you want. For example:
function BuildHandler (i) { return function () { alert(i); };
for (i= 1; i < 100; i++) {
var my_element = document.createElement ("td");
row.appendChild (my_element);
my_element.onclick = BuildHandler(i);
}
if I were you I will use Jquery (or prototype or whatever js frameworks that available)
on each elements you should add attributes like myid for example so that when you did on click you can retrive it.
for(i=1; i ++ ; i<100){
var myelement = "<td myid='something"+i+"' class='myTD'></td>" ;
row.append(myelement);
}
....
$(document).ready(function(){
$('.myTD').click(function(){
var id = $(this).attr('myid');
my_function(id);
});
});
I did this trick on my web app :)