Loop with delay inside other loop - javascript

I'm trying to transform this loop that changes the value of a varible number of inputs:
$("#wrap input").each(function( index ) {
for( i = 10 ; i > 0 ; i--) {
$(this).val(i);
console.log( index + ": " + $(this).val() );
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="wrap">
<input type="text">
<input type="text">
<input type="text">
</div>
I need to have a small delay between each iteration. I'm trying some variatons of this:
$("#wrap input").each(function( index ) {
var a = $(this);
var i = 10;
var loop = setInterval(function() {
a.val(i);
console.log( index + ": " + a.val() );
i--;
if(i==0) clearInterval( loop );
},1000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="wrap">
<input type="text">
<input type="text">
<input type="text">
</div>
It doesn't work like I need.
I need to change the value in order, first input changes to 10 (delay) changes to 9 (delay)... second input changes to 10 (delay) changes to 9 (delay)... and so on. Hope you get the idea.
I'm thinking to assign them different ids via jQuery and then make a loop through each one separately, but I need a script as brief as possible.
Any changes to the structure have to be done with js.
The value update has to be in order
There must be a delay in each iteration
The code has to be as simple as possible
I'm a little stuck, I appreciate any hint.
Final update:
#acontell gave a perfect answer for the example I was dealing with in my question. Yet I realized why I didn't use next() since the begginnig: in the real project the inputs are not immediate siblings, they are each inside a couple of containers. Anyway the loop proposed by #acontell suits fine, I just had to put the elements in an array, like this:
var list = [];
$("#wrap input").each(function(index) {
list.push($(this));
});
var i = 10; // Countdown start
var n = 0; // Array index
var loop = setInterval(function() {
if (n > list.length - 1) {
clearInterval(loop);
} else {
list[n].val(i--);
if (i < 0) {
n++;
i = 10;
}
}
}, 100);
<div id="wrap">
<div><div><div><input type="text"></div></div></div>
<div><div><div><input type="text"></div></div></div>
<div><div><div><input type="text"></div></div></div>
<div><div><div><input type="text"></div></div></div>
<div><div><div><input type="text"></div></div></div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

I'm not really sure of what you're after, but if you want to wait for each counter (in each input) to finish and then start the next counter, a linked list comes to mind.
Jquery provides the method next that gets you the next sibling. That could help you to build the linked list. The iteration over the list could be done using an interval that would be cleared when there were no more siblings. Putting it all together:
var $element = $("#wrap input:first"),
countdownLimit = 10;
var loop = setInterval(function() {
if (!$element.length) {
clearInterval(loop);
} else {
$element.val(countdownLimit--);
if (countdownLimit < 0) {
$element = $element.next('input');
countdownLimit = 10;
}
}
}, 1000);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="wrap">
<input type="text">
<input type="text">
<input type="text">
</div>
There can be as many inputs as you like as long as they all are siblings.

Any time you need to have a delay in iterations, it makes sense to use a self-calling function instead of a loop.
function doStuff(data, i){
//do things here
console.log(i)
if(--i) setTimeout(function(){doStuff(data, i)}, 1000)
else nextStep(data)
}
function nextStep(data){
//after the loop ends, move to the next step of your code
}
doStuff([], 10)

Have you tried the equivalent of thread sleep?
var millisecondsToWait = 500;
setTimeout(function() {
// Whatever you want to do after the wait
}, millisecondsToWait);

Related

Displaying the iterations of a while loop continuously in a text box in Javascript

Hi I'm trying to display the iterations of a while loop continuously in a text box. However, the browser waits till the loop finishes then displays the final iteration only.
I'm using this loop to run a complex function which means the iterations will be slower and can be seen if displayed in the console:
For example, I'm trying to do something like this.
in HTML:
<p>
<label>This is generation number</label>
<input type = "number"
id = "generation"
/>
</p>
in Javascript:
function Run(){
var i=0;
while (i<500) {
document.getElementById('generation').value= i;
i++
}
}
You can not do it synchronously (No while loop). This locks up the browser and does not allow anything else to happen. No draw cycle can happen and it looks like nothing it changing.
Try breaking it into a function that only does one step and then call it over and over through a timeout.
var updateEl = document.getElementById("generation");
var i;
function doStep() {
//super crazy code
updateEl.value = i;
i++;
if (i < 500) {
timeout = setTimeout(doStep);
}
}
function Run() {
i = 0;
doStep();
}
If you need this process to be as fast as possible you want setTimeout. If you want to synchronize with the animation buffer of canvas or speed is not a real issue then use requestAnimationFrame.
UPDATE
To match what #Brian is saying in his comment below:
var updateEl = document.getElementById("generation");
function doStep(i) {
//super crazy code
updateEl.value = i++;
if (i < 500) {
timeout = setTimeout(() => doStep(i));
}
}
function Run() {
doStep(0);
}
<p>
<label>This is generation number</label>
<input type="number" id="generation"/>
</p>
<button onclick="Run()">Run</button>

How do I delay an execution in Javascript?

i swear i've being looking for the way to solve this many hours before I ask here, I found some codes but still nothing fits to what I need... the thing is that I have some boxes and 1 button for rolling a random style... when I click the button the following code starts;
function roll(){
for (i = 0; i < 4; i++) {
setInterval(function(){
res = Math.floor(Math.random() * 10) + 1;
/*document.getElementById("slot-" + res).style.border = "5px solid red";
document.getElementById("slot-" + res).style.color = "red";*/
document.getElementById("slot-" + res).className = "col-sm-2 slot-active";
}, 500);
document.getElementById("slot-" + res).className = "col-sm-2 slot";
}
};
It selects at random between 1 and 10 and changes style class to that box... so... in the line after the loop, i pretend to put original stile back so it gives a look of a random selection... but it doesnt executes, if I put it inside the for loop it executes inmediatly and seems like nothing is happening... im Javascript noob and i'm trying this to learn and practice!... thanks in advance for your help!...
You can create a setInterval that will randomly set the selected class, and have a setTimeout to end the execution of said interval, something like this: https://jsfiddle.net/canastro/xxdbjm4n/1/
const refreshIntervalId = setInterval(() => {
$('.selected').removeClass('selected');
const res = Math.floor(Math.random() * 10) + 1;
$(`.slot-${res}`).addClass('selected');
}, 500)
setTimeout(() => {
clearInterval(refreshIntervalId);
}, 3000);
You could use setTimeout(function(){dosomething}, timeout), btw. add console.log('something') to see if the functions are actually executed.
btw. if you're using interval remember you might need to cancel it if you dont want it running forver, you could as well use recurring function (a function calling itself) with some condition on when to do processing or now.
btw2. every time you create interval assign it to some variable or something so you can cancel it!
your answer should be like this.you need to see javascript variable scope.
function roll(){
for (i = 0; i < 4; i++) {
var res = Math.floor(Math.random() * 10) + 1;
function active(res){
setTimeout(function(){
document.getElementById("slot-" + res).className = "col-sm-2 slot-active";
}, 500);
document.getElementById("slot-" + res).className = "col-sm-2 slot";
}
active(res);
}
};
div{
width:50px;
height:50px;
background:#ccc;
float:left;
margin:5px;
}
.slot{
background:#0f0;
}
.slot-active{
background:#f00;
}
<button onclick="roll()" style="display:block;">roll</button>
<div id="slot-1"></div>
<div id="slot-2"></div>
<div id="slot-3"></div>
<div id="slot-4"></div>
<div id="slot-5"></div>
<div id="slot-6"></div>
<div id="slot-7"></div>
<div id="slot-8"></div>
<div id="slot-9"></div>
<div id="slot-10"></div>

jquery, while loop running in the background, simultaneous while loops

1) How make that while loop would run in background, and the webpage would respond to user clicks despite while loop.
If I start the characters generating while loop, I am not able to input the data to the "input", because the generating loop occupies all resources. Actually, whenever I click "start", I am getting the message that the webpage is not responding asking if I want to stop it. After choosing "to stop", I see that the characters are generated. Nevertheless, it is very difficult to input the characters to the input field and to stop the program with "stop" trigger, and usually webpage crashes.
2) How to make several jquery while loops to run simultaneously, and additionally, webpage should be responsive and accessible to user.
<!DOCTYPE html>
<html>
<head>
<script src="theme/assets/js/jquery/jquery-2.2.3.min.js"></script>
</head>
<body>
<div id="Start"> <b> Start </b> </div>
<div id="Stop"> <b> Stop </b> </div>
<br><br> <div id="random"> </div>
<br><br> <input id="input" type="text" size="500">
<script>
// how to manage without global variables? how to pass variables to the function
var flag = false;
var charstr = "zxcvbnm,\.\/asdfghjkl;\'qwertyuiop[]\\`ąčęėįšųū90\-ž";
var charstrL = charstr.length;
$("#Start").click( function() {
$("#lesson").text("clicked Start");
flag =true;
$(this).val('flag');
while(flag){
setInterval(function() { // this code is executed every 500 milliseconds:
var rand = Math.random();
var num = Math.floor( ( Math.random() * (charstr.length -1) ) );
$("#lesson").text(charstr[num]);
}, 500);
}//while
}); // $("#Start").click( function() {
$("#Stop").click( function(){
flag=false;
$(this).val('flag');
alert('clicked Stop');
});
</script>
</body>
</html>
You can't make a while loop run in the background if it's doing DOM manipulation, because there's only one main UI thread in browser JavaScript.
You also probably don't want to, because this code:
while (flag) {
setInterval(function() { // this code is executed every 500 milliseconds:
var rand = Math.random();
var num = Math.floor( ( Math.random() * (charstr.length -1) ) );
$("#lesson").text(charstr[num]);
}, 500);
}
continuously adds additional timers to call that code every 500ms. In a very short period of time, your browser will become completely non-responsive.
Just set up setInterval, and have the code inside decide whether to run based on flag:
setInterval(function() { // this code is executed every 500 milliseconds:
if (flag) {
var rand = Math.random();
var num = Math.floor( ( Math.random() * (charstr.length -1) ) );
$("#lesson").text(charstr[num]);
}
}, 500);
You can have several of those, though if you have a lot of them you might consider having fewer and just having them do more than one thing each time.
This is a working example for a program prompting user to input characters utilizing setInterval function instead of while loop. The application is to improve the typing skills.
<!DOCTYPE html>
<html>
<head>
<script src="theme/assets/js/jquery/jquery-2.2.3.min.js"> </script>
</head>
<body>
<div id="Start"> <b> Start </b> </div>
<br><div id="lesson"> </div>
<input id="input" type="text" size="500">
<span id="Stop"> Stop </span>
<span id="Clear">
Clear </span>
<script>
// how to manage without global variables ? how to pass vaiables to the function
var flag = false;
var charstr = "zxcvbnm,\.\/asdfghjkl;\'qwertyuiop[]\\`ąčęėįšųū90\-ž";
var charstrL = charstr.length;
$("#Start").click( function() {
flag =true;
$(this).val('flag');
if(flag){
setInterval( function() { // this code is executed every 500 milliseconds:
if (flag) {
var rand = Math.random();
var num = Math.floor( ( Math.random() * (charstr.length -1) ) );
$("#lesson").text("\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0"+charstr[num]);
} //if (flag) {
}, 500);
} // if (flag)
}); // $("#Start").click( function() {
/*
// does not work, because creates infinite loops, each with 500ms waiting time.
// In miliseconds this accumulates to hours and thousands of loops generated usign while
while(flag){
setInterval(function() { // this code is executed every 500 milliseconds:
var rand = Math.random();
var num = Math.floor( ( Math.random() * (charstr.length -1) ) );
$("#lesson").text(charstr[num]);
}, 500);
}//while */
//
$("#Stop").click( function(){
flag=false;
$(this).val('flag');
});
$("#Clear").click( function(){
$("#input").val('');
$("#lesson").text(' ');
});
</script>
</body>
</html>

jQuery slider - last to first transition

I created this slider (didn't want to use plugins):
function slider(sel, intr, i) {
var _slider = this;
this.ind = i;
this.selector = sel;
this.slide = [];
this.slide_active = 0;
this.amount;
this.selector.children().each(function (i) {
_slider.slide[i] = $(this);
$(this).hide();
})
this.run();
}
slider.prototype.run = function () {
var _s = this;
this.slide[this.slide_active].show();
setTimeout(function () {
_s.slide[_s.slide_active].hide()
_s.slide_active++;
_s.run();
}, interval);
}
var slides = [];
var interval = 1000
$('.slider').each(function (i) {
slides[i] = new slider($(this), interval, i);
})
The problem I have is that I don´t know how to get it after the last slide(image), it goes back to the first slide again. Right now, it just .hide and .show till the end and if there is no image it just doesn´t start again.
Can someone help me out with a code suggestion to make it take the .length of the slider(the number of images on it) and if it is the last slide(image), then goes back to the first slide(image)... like a cycle.
Edit: Slider markup
<div class="small_box top_right slider">
<img class="fittobox" src="img/home10.jpg" alt="home10" width="854" height="592">
<img class="fittobox" src="img/home3.jpg" alt="home3" width="435" height="392">
<img class="fittobox" src="img/home4.jpg" alt="home4" width="435" height="392">
</div>
Created a fixed version for you here.
The easiest way to do this is to run a simple maths operation where you currently have
_s.slide_active++;
Instead, I get _s.slide_active, add 1, then run that through modulus (%) to the total length — which gives the remainder:
_s.slide_active = (_s.slide_active + 1) % _s.slide.length;
Take a look at this Fiddle link, this will help you create the slider in a cyclic way.If the slider reaches the last image it will start again from the first image.
var index = $selector.index();
if (index == (length - 1)) {
$('img').first().removeClass('invisible').addClass('visible');
}
I hope this will help you more. All the best.
You need to get to 0 after length-1.
One simple way to do that is to work modulo length:
_s.slide_active++;
_s.slide_active %= length;
not tested but hope helpful :
function slider(sel, intr , i){
...
this.count = this.selector.children().length;
this.run();
}
slider.prototype.run = function(){
var _s = this;
this.slide[this.slide_active].show();
setTimeout(function(){
_s.slide[_s.slide_active].hide()
if(_s.slide_active == this.count)
_s.slide_active = 0;
else
_s.slide_active++;
_s.run();
}, interval);
}

Javascript Recursion Improvement

Someone at work jokingly sent out an email with a html file intended to crash your browser that was the following
<html>
<script type="text/javascript">
function crash(){
for(i=0;i<5000000001;i++){
document.write(i);
}
}
</script>
<body onload="crash();">
</body>
</html>
Anyways it doesn't do a great job of it in Chrome and a conversation arose that it created a friendly competition to see who could write javascript to make a page count to 5,000,000,000 as quickly as possible without causing the browser to become unresponsive or crash.
I came up with the following piece of javascript that is intended to be used in Chrome.
<html>
<script type="text/javascript">
function countToFiveBillion(counter, num){
if(num < 5000000000)
{
num++;
if(num % 18700 == 0){
counter.innerHTML = num;
setTimeout(function() {countToFiveBillion(counter, num)}, 1);
} else {
countToFiveBillion(counter, num);
}
}
}
function initiateCountDown()
{
var counter = document.getElementById("counter");
var num = +counter.innerHTML;
countToFiveBillion(counter, num);
}
</script>
<body onload="initiateCountDown();">
<div id="counter">0</div>
</body>
</html>
The reason that this will only run in chrome is that I'm using the setTimeout call to avoid creating a stackoverflow in chrome. (Chrome also allows you the largest stack for recursive calls out of all of the browsers).
Is there any way for me to make this count any quicker? I think that I can increase the amount counted a little before it causes an overflow (somewhere less than 100 though) The only stipulation is that is has to display as many numbers as possible as it counts.
Improved Code:
<html>
<script type="text/javascript">
var counter;
var num = 0;
function countToFiveBillion(){
if(num < 5000000000)
{
num++;
if(num % 18701 == 0){
setTimeout("countToFiveBillion()", 1);
counter.value = num;
} else {
countToFiveBillion();
}
} else {
counter.value = "number greater than 5 Billion";
}
}
function initiateCountDown()
{
counter = document.getElementById('counter');
countToFiveBillion();
}
</script>
<body onload="initiateCountDown();">
<input type="text" id="counter" value="0" />
</body>
</html>
Made count and element globabl
Switched to text input instead of div
moved update UI to after setting the callback
Don't use .innerHTML = ... to display the number. According to this test, setting the value property of an input element is more efficient.
<input type="text" id="counter" value="0" />
Instead of constructing a new function, I recommend to use global / local variables, and passing a function reference as an argument to setTimeout, or use setInterval at init.
Swap setTimeout("countToFiveBillion()",1) for setTimeout(countToFiveBillion,0).
Explanation: "countToFiveBillion()" is inefficient; First, the string gets converted to a function and called, then another function call follows. The suggested function runs only has to call a function, without creating new ones. It's also called a split second faster.
Lift the limit (I was able to increase 18701 to 20000). After lifting the limit to such a rounded number, I noticed that the counter value is updated between each time-out.
Fixed some errors in the implementation (replaced .innerHTML with .value at the else-block).
Relevant code:
<input type="text" id="counter" />
<script>
var counter, num = 0;
function countToFiveBillion(){
if(num < 5e9)
{
if(++num % 18701 == 0){
setTimeout(countToFiveBillion, 0);
counter.value = num;
} else {
countToFiveBillion();
}
} else {
counter.value = "number greater than 5 Billion";
}
}
function initiateCountDown(){
counter = document.getElementById('counter');
counter.value = num; //Init, show that the script is
countToFiveBillion();
}
window.onload = initiateCountDown;
</script>
Fiddle: http://jsfiddle.net/KTtae/
Webworker example, index.html
<!DOCTYPE HTML>
<html>
<head>
<title>5 billion</title>
</head>
<body>
<input type="text" id="counter" value="0" />
<script type="text/javascript" charset="utf-8">
var
iCounter = document.getElementById('counter')
, counter = new Worker('worker.js');
iCounter.value = 0;
counter.addEventListener('message', function (e) {
iCounter.value = e.data;
}, false);
</script>
</body>
</html>
worker.js:
for (var i = 0; i < 5e9; i++) {
if (i % 18701 === 0) {
postMessage(i);
}
}
The counting can be splited in multiple workers if needed.

Categories