Help with simple javascript loop - javascript

I have a simple javascript that I'd like to loop for multiple elements. Here's my code:
<script type='text/javascript'>
for(i = 1; i < 100; i++)
{
$('#link'+i).click(function() {
$('#container').removeClass();
$('#container').addClass('templateid'+i);
});
}
</script>
What I'd like to achieve is the same addClass function for multiple id's (e.g. link2, link3, link4), with the corresponding class (e.g. template2, template3, template4).
Any help would be hugely appreciated!
For reference, an individual call like this one, does work, so I don't see why the loop above doesn't function the same:
<script type='text/javascript'>
$('#link2').click(function() {
$('#container').removeClass();
$('#container').addClass('templateid2');
});
</script>

The problem here is that i inside the anonymous function for the on click handler, is a reference to the loops i and therefore will be 100 for all click handlers after the loop finished.
That a common "mistake" and requires a copy of i on every iteration of the loop in order to work correctly.
function createHandler(i) {
$('#link'+i).click(function(e) {
$('#container').removeClass();
$('#container').addClass('template'+i); // this 'i' won't change anymore
e.preventDefault(); // thus the code will add the correct class
return false;
});
}
for(var i = 0; i < 100; i++) {
createHandler(i); // one could also create a closure here
}
See: JavaScript Garden: Closures - Avoiding the Reference Problem.

Maybe you've forgotten to separate counter from string
$('#link[' + i + ']').click(function() {
$('#templateid').removeClass();
$('#templateid').addClass('templateid[' + i + ']');

are you using prototype? $('#link') should return the element with id="link" and [i] further narrows it down to the attribute i.
if you want to use the variable i from the loop to find your link2, link3,.. try:
$('#link' + i).click { ...
also, your loop seems to be endless.

Related

Replace contents of <div> in a loop while waiting

I'm having difficulty figuring out my issue, or even finding solutions for it, which leads me to believe I may be going in the wrong direction.
On a cshtml page I have an ajax function tied to a button click. That routes back to my controller and a json string array is returned back to the client.
On the page itself I have <pre id="replaceMe">***</pre> defined. I am attempting to iterate through the json array doing $("#replaceMe").replaceWith('<pre id="replaceMe">' + data[i] + '</pre>');
Technically speaking this works, but only in the sense that the last update is visible. I might as well just go straight to the last element of the array.
I've tried using setTimeout to no avail, no changes and then suddenly the last element is displayed. I've found some sleep like functions that mimic the same basic behavior, all with similar results. I did see some recommendations for an async sleep, but none of my browsers seem to like the async and give me an error about a missing ;.
I then thought I could do something like
function updateSection(data) {
for (var i = 0; i < data.length; i++){
var section = $("#replaceMe");
section.fadeOut(500);
section.replaceWith('<pre id="replaceMe">' + data[i] + '</pre>');
section.fadeIn(500);
}
}
That however has the same end result. No apparent change and then suddenly it's the last element in the array.
I'm clearly going about this wrong, otherwise I'd find an example fairly readily I think, so what should I be doing instead?
To clarify and sum up, I want to replace the content of the <pre></pre> with text that's contained in an array. I want each iteration to be visible long enough for a human to see it and observe the changes (~1000ms) before going to the next iteration.
If, for example the array contains "Tom", "Dick", "Harry", then I would like for the page to have
<pre id="replaceMe">Tom</pre> for 1 second, then that element is replaced with
<pre id="replaceMe">Dick</pre> for 1 second, then that element is replaced with
<pre id="replaceMe">Harry</pre>
I am NOT looking for
<pre id="replaceMe">Tom</pre>
<pre id="replaceMe">Dick</pre>
<pre id="replaceMe">Harry</pre>
setTimeout in a for loop runs after the for loop execution completed. so, you always see the last value. to solve this, you can use $.each method which provides a callback function or use an Immediately Invoked Function Expression.
more detailed info: https://codehandbook.org/understanding-settimeout-inside-for-loop-in-javascript/
var data=[];
for(var i=0; i<10; i++){
data.push(i+' lorem ipsum doloret');
}
$.each(data, function(i, el){
setTimeout(function(){
$("#replaceMe").replaceWith('<pre id="replaceMe">' + data[i] + '</pre>');
},500 + ( i * 1000 ));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<pre id="replaceMe">***</pre>
You can do that using setInterval function:
var words = ['interating', 'and', 'replacing', 'text'];
var replace = document.querySelector('#replace');
var count = 0;
function replaceText() {
replace.innerHTML = words[count];
if(count === words.length - 1)
count = 0;
else
count++;
}
setInterval(replaceText, 1000);
Updating:
You don't need to replace all the element, you can replace only the content, using the atribute innerText.
//pass in the data to loop over, and the index to show
function updateSection(data, index) {
//get a reference to the section
var $section = $('#replaceMe');
//fade out the section over 500 milliseconds, then perform callback on finish
$section.fadeOut(500, () => {
//change the text
$section.text(data[index]);
//fade in the section over 500 milliseconds, and then advance the index
//use modulus to reset the index to 0 when it reaches beyond the array
$section.fadeIn(500, () => updateSection(data, ++index % data.length));
});
}
updateSection(['Tom', 'Dick', 'Harry'], 0);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="replaceMe">***</div>

Loop through a set of elements jQuery [duplicate]

This question already has answers here:
jQuery Looping and Attaching Click Events
(4 answers)
Closed 7 years ago.
I'm trying to loop through 12 classes, named .video-link0 through video-link11, where each one gets the treatment:
$('.video-link[n]').click(function() {
$('.video-link[n]').addClass('show');
});
Essentially, I want the following behavior:
When .video-link1 is clicked, addClass('show') to video-link1
When .video-link2 is clicked, addClass('show') to video-link2
and so on, as if I had 12 functions that looked like this:
$('.video-link1').click(function() {
$('.video-link1').addClass('show');
});
$('.video-link2').click(function() {
$('.video-link2').addClass('show');
});
... and so on
I want to write a single loop that replaces the need to write this out as 12 separate cases.
The following does not yield the result I'm looking for:
var elems = 12;
for(var i = 0; i < elems; i++){
$('.video-link' + i).click(function() {
$('.video-link' + i).addClass('show');
});
};
** UPDATE **
This is not a duplicate question, or else the above question referenced does not address my requirement. I am not trying to move up and down the DOM with next. Rather, I want to write a single loop that iterates through 12 classes numbered 0-11 using i to enumerate the cases.
** UPDATE **
This works for me, and is using a suggestion by Lloyd Banks (I needed the i enumerator PLUS the this keyword):
for (var i = 0; i < 12; i++) {
$('.video-link'+i).click(function() {
$(this).addClass('show');
});
}
You can use starts with ^= selector and reference each with $(this)
$("[class^='video-link']").click(function() {
$(this).addClass('show');
});
You can use $(this) to reference the current (targeted) element inside of a event callback:
$('.video-link').click(function() {
$(this).addClass('show');
});
You can use
function(numberOfElements){
for(var i = 1; i <= numberOfElements; i++){
$('.video-link' + i).on('click', function(){
$(this).addClass('show');
});
}
}
You should also be using the .on binding event instead of .click. If you are generating your element after initial page load and use .click, the event handler wouldn't be registered.

Loop and Functions

I'm having troubles gathering information about clicked eventListeners.
I have this loop which builds an array:
myButtonList = document.getElementsByTagName('a');
myAnchorList = [];
for (i=0; i < myButtonList.length;i++) {
if (myButtonList[i].getAttribute('class') == 'flagged') {
myAnchorList.push(myButtonList[i]);
}
}
For each <a> put into myAnchorList array, I also create another array storing other informations from the same tag (classe and other atrributes).
Here's where I'm struggling. I'm trying to set up an eventListener to send me back those information when those <a> are being clicked. But somehow, the fact that I create a function (for the eventListener) within a loop breaks everything.
for (i=0; i < myAnchorList.length; i++) {
myAnchorList[i].addEventListener("click", function(i){
console.log(alpha+' - '+beta[i]+" - "+charlie[i]);
});
}
My values will either be undefined or some other values which will be the same for each buttons I clicked. alpha is working well as it doesn't depend on any iteration of the loop, but not the others.
Can anybody see what I'm doing wrong here?
for (var i = 0; i < myAnchorList.length; i++) {
(function (i) { //Passes i to your function
myAnchorList[i].addEventListener("click", function () {
console.log(alpha+' - '+beta[i]+" - "+charlie[i]);
});
})(i);
}
The variable "i" in closure that you created in the loop will always retrieve the last value(myAnchorList.length - 1). You shouldn't create closure in a loop, and you can use a "factory" method to create closure instead.

basic jquery looping question around 'click'

Basic question, but I have been pounding my head for a bit so thought id bring it here.
html looks like this (edit, fixed the closing quotes)
<span class='deleteimage-119'>delete</span>
<span class='deleteimage-120'>delete</span>
<span class='deleteimage-121'>delete</span>
<span class='deleteimage-122'>delete</span>
<span class='deleteimage-123'>delete</span>
javascript/jquery looks like this
iids = ['119','120','121','122','123'];
for (i=0; i<iids.length; i++) {
place = iids[i];
$(".deleteimage-" + place).click(function () {
alert(place);
});
}
The click functionality gets attached to each individual span, but the alert after clicking just shows the last item in the array.
You have a scoping issue. By the time the callback fires, place has the last value from the loop.
You need to create a new variable for the closure; one variable per iteration, each of which will then be "caught" by the closure and used in the callback.
It would be nice if the solution were this:
var iids = ['119','120','121','122','123']; // don't forget `var` please
for (var i=0; i<iids.length; i++) {
var place = iids[i]; // local variable?
$(".deleteimage-" + place).click(function () {
alert(place);
});
}
Alas, Javascript has no block scope so there's still only one variable here called place, and it keeps getting updated as the loop runs.
So, you have to use a function instead:
var iids = ['119','120','121','122','123'];
function f(place) {
// NOW `place` is a local variable.
$(".deleteimage-" + place).click(function () {
alert(place);
});
}
for (var i=0; i<iids.length; i++) {
f(iids[i]);
}
There are neater ways to employ this function approach using closures and bound variables, and the other answers cover those neater ways quite well. My answer has focused on explaining the issue.
The issue is with scopes and closure.
In JS the scope is # function level and not block level.
Try this:
var iids = ['119','120','121','122','123'];
for (i=0; i<iids.length; i++) {
place = iids[i];
var clickFn = function(a){
return function(){
alert(a);
}
}(place);
$(".deleteimage-" + place).click(clickFn );
}
This is because the click is occurring after the loop is completed, so you're alerting place = iids[iids.length - 1]. In order to achieve the result you're looking for you need to create a function closure and pass place in as a parameter:
iids = ['119', '120', '121', '122', '123'];
for (i = 0; i < iids.length; i++) {
place = iids[i];
(function(_place) {
$(".deleteimage-" + _place).click(function() {
alert(_place);
});
} (place));
}
Inside the loop, you are binding the click event to those span, but the events are fired only after the loop is complete. So, it will always show the last value.
As others have mentioned, you have a scoping issue. Unless all of those classes have a unique meaning, I'd move the place value to a data attribute and leave deleteimage as the class name. Something like:
<span class='deleteimage' data-place='119'>delete</span>
<span class='deleteimage' data-place='120'>delete</span>
<span class='deleteimage' data-place='121'>delete</span>
<span class='deleteimage' data-place='122'>delete</span>
<span class='deleteimage' data-place='123'>delete</span>
$(".deleteimage").click(function() {
var place = $(this).data("place");
alert(place);
});
If the place values aren't unique values, then this answer doesn't apply.

Why aren't these two bits of JavaScript equivalent?

in jquery 1.4.2, ff 3.6.6:
The following code produces three divs, which write messages to the firebug console as you would expect. However, if you uncomment out the loop and comment out the 3 lines doing it manually, it doesn't work - mousing over any of the divs results in "three" being written to the console.
Why are these two methods any different than each other? In each one you use a selector to find the element and add an event to it.
<head>
<script type="text/javascript" src="/media/js/jquery.js"></script>
<script>
$( document ).ready( function() {
$("#one").mouseenter(function(){console.log("one")})
$("#two").mouseenter(function(){console.log("two")})
$("#three").mouseenter(function(){console.log("three")})
// names=['one','two','three'];
// for (k in names){
// id=names[k]
// $("#"+id).mouseenter(function(){console.log(id)})
// }
})
</script>
</head>
<body>
<span id="one">ONE</span>
<p><span id="two">TWO</span></p>
<p><span id="three">THREE</span></p>
</body>
You would be having a very common closure problem in the for in loop.
Variables enclosed in a closure share the same single environment, so by the time the mouseenter callback is called, the loop will have run its course and the id variable will be left pointing to the value of the last element of the names array.
This can be quite a tricky topic, if you are not familiar with how closures work. You may want to check out the following article for a brief introduction:
Mozilla Dev Center: Working with Closures
You could solve this with even more closures, using a function factory:
function makeMouseEnterCallback (id) {
return function() {
console.log(id);
};
}
// ...
var id, k,
names = ['one','two','three'];
for (k = 0; k < names.length; k++) {
id = names[k];
$("#" + id).mouseenter(makeMouseEnterCallback(id));
}
You could also inline the above function factory as follows:
var id, k,
names = ['one','two','three'];
for (k = 0; k < names.length; k++) {
id = names[k];
$("#" + id).mouseenter((function (p_id) {
return function() {
console.log(p_id);
};
})(id));
}
Any yet another solution could be as #d.m suggested in another answer, enclosing each iteration in its own scope:
var k,
names = ['one','two','three'];
for (k = 0; k < names.length; k++) {
(function() {
var id = names[k];
$("#" + id).mouseenter(function () { console.log(id) });
})();
}
Although not related to this problem, it is generally recommended to avoid using a for in loop to iterate over the items of an array, as #CMS pointed out in a comment below (Further reading). In addition, explicitly terminating your statements with a semicolon is also considered a good practice in JavaScript.
This happens because when the code of the anonymous function function(){console.log(id)} is executed, the value of id is three indeed.
To can use the following trick to enclose the value on each loop iteration into the anonymous callback scope:
names=['one', 'two', 'three'];
for (var k in names) {
(function() {
var id = names[k];
$("#"+id).mouseenter(function(){ console.log(id) });
})();
}

Categories