The code to return the tree structure to console.log with a recursive function as shown below. I added setTimeout because I need some delays during the code processing, but when I add it, the order of processing is output in a completely different format. Also, the delay time does not seem to be constant.
The intended value is
If there is a child in the node after the first category title is searched, the second category title is searched by recursive, and the children value is inquired again, and then the third .. .. If setTimeout is added, The whole will be sown first, then the second whole, then the third whole, and so on.
Why is this happening?
var time = 0;
function searchTree(v, t){
$(v).each(function(i,k){
setTimeout(function(){
if (t == 'clone'){
console.log(k.sCategoryTitle);
if (k.children){
searchTree(k.children,'clone');
}
}
}, time = time + 100);
});
}
sample code : http://jsfiddle.net/uahg5qd9/3/
you have make your function synchronous.
try below code, if it will help you. here i have removed loop and put another synchronous function to make it synchronous.
var time = 100;
function searchTree(v, t){
var i=0;
function loop(){
if(i<v.length){
let k = v[i];
if (t == 'clone'){
console.log(k.sCategoryTitle);
if (k.children){
setTimeout(function(){
time = time + 100;
searchTree(k.children,'clone');
},time);
}else{
i++;
loop();
}
}else{
i++;
loop();
}
}else{
i++;
loop();
}
}
loop();
}
Related
I currently try to get into JavaScript a bit. (vanilla)
So at the moment what i want to do is swap different element nodes.
As an example imagine a list with different entries, which later shall be swapped.
For this for element.length times random elements should be swapped. After each of this swaps should be a timeout.
The problem which i now encounter is that the list only updates after the shuffle function finishes.
It is intended to swap a pair, wait for 1 second and then do the next swap.
function shuffle(element){
disableButtons();
let clicked_elmnt = document.getElementsByTagName(element);
console.log(clicked_elmnt);
if(clicked_elmnt.length !== 1 && clicked_elmnt !== 0){
for (let i = 0; i<clicked_elmnt.length;++i){
setTimeout(function(){doIt(element,randomNumber(clicked_elmnt.length),randomNumber(clicked_elmnt.length))}, 1000)
}
}
enableButtons();
}
This code should check if there is a list in a list and then roll new numbers if it tries to swap a parent with its child. (Seems to be working)
function doIt(element,first,second){
let clicked_elmnt = document.getElementsByTagName(element);
if(clicked_elmnt[first].children.length > 0){
if(clicked_elmnt[first].firstElementChild.isSameNode(clicked_elmnt[second].parentNode)){
doIt(element,randomNumber(clicked_elmnt.length),randomNumber(clicked_elmnt.length));
}else{
if(clicked_elmnt[second].children.length > 0){
if(clicked_elmnt[second].firstElementChild.isSameNode(clicked_elmnt[first].parentNode)){
doIt(element,randomNumber(clicked_elmnt.length),randomNumber(clicked_elmnt.length));
}else{
doSwap(clicked_elmnt[first],clicked_elmnt[second]);
}
}else{
doSwap(clicked_elmnt[first],clicked_elmnt[second]);
}
}
}else{
if(clicked_elmnt[second].children.length > 0){
if(clicked_elmnt[second].firstElementChild.isSameNode(clicked_elmnt[first].parentNode)){
doIt(element,randomNumber(clicked_elmnt.length),randomNumber(clicked_elmnt.length));
}else{
doSwap(clicked_elmnt[first],clicked_elmnt[second]);
}
}else{
doSwap(clicked_elmnt[first],clicked_elmnt[second]);
}
}
}
And the swap then happens in the next function. Here they are swapped.
function doSwap(n1, n2){
console.log("swap");
const afterN2 = n2.nextElementSibling;
const parent = n2.parentNode;
if (n1 === afterN2) {
parent.insertBefore(n1, n2);
} else {
n1.replaceWith(n2);
parent.insertBefore(n1, afterN2);
}
}
Hope someone can help me here without that much weird stuff.
Cheers!
My understanding is you wish that the doIt calls, the callbacks of setTimeout to be 1 second apart from each other. The way setTimeout works is the callbacks are queued to be executed x milliseconds after setTimeout is called. If you want one function to be called 1 second after the previous function finishes, you could call another setTimeout from inside the callback.
Here's your example adjusted as such - the doItLater function includes a setTimeout call, and will recursively call itself clicked_elmnt.length times. You may be able to set it up more cleanly for a real scenario, but this fits without deviating too much from your example.
function shuffle(element) {
disableButtons();
let clicked_elmnt = document.getElementsByTagName(element);
if (clicked_elmnt.length !== 1 && clicked_elmnt !== 0) {
const doItLater = function (i) {
setTimeout(() => {
doIt(element, randomNumber(clicked_elmnt.length), randomNumber(clicked_elmnt.length));
if (i + 1 < clicked_elmnt.length) {
doItLater(i + 1);
}
}, 1000);
}
doItLater(0);
}
enableButtons();
}
If you want to only call enableButtons after the last doIt call finishes, you could move that call to the else clause of if (i + 1 < clicked_elmnt.length).
I have succeeded in cobbling together pieces of code that achieve my goal. However, I would like some advice from more advanced vanilla JS programmers on how I can go about reaching my goal in a better way.
To start, I want to introduce my problem. I have a piece of text on my website where a portion is designed to change every so often. For this, I am running through a loop of phrases. To run this loop continuously, I first call the loop, then I call it again with setInterval timed to start when the initial loop ends. Here is the code I've got, which works even if it isn't what could be considered quality code:
function loop(){
for (let i = 0; i < header_phrases.length; i++){
(function (i) {
setTimeout(function(){
header_txt.textContent = header_phrases[i];
}, 3000 * i);
})(i);
};
}
loop();
setInterval(loop, 21000);
Is there a better way to right this code for both performance and quality? Do I need to use async? If so, any material I can see to learn more? Thanks!
You can implement the same logic using recursion.
function recursify(phrases, index = 0) {
header_txt.textContent = phrases[index];
setTimeout(function () {
recursify(phrases, index < phrases.length - 1 ? index + 1 : 0);
}, 300)
}
recursify(header_phrases);
The function 'recursify' will call itself after 300 miliseconds, but everytime this function gets called, the value of index will be different.
If I understand your requirement correctly, you want top populate an element from an array of values.
A simple way to do this is:
doLoop();
function doLoop() {
var phraseNo=0;
setTimeout(next,21000);
next();
function next() {
header_txt.textContent = header_phrases[phraseNo++];
if(phraseNo>=header_phrases.length) phraseNo=0;
}
}
This simply puts the next() function on the queue and waits.
The call to next() before the function simply starts it off without waiting for the timeout.
this is assuming that header_txt and header_phrases are not global vars. using global vars isn't a good idea.
var repeatIn = 3000;
phraseUpdater();
function phraseUpdater() {
var updateCount = 0,
phrasesCount = header_phrases.length;
setHeader();
setTimeout(setHeader, repeatIn);
function setHeader() {
header_txt.textContent = header_phrases[updateCount++ % phrasesCount] || '';
}
}
guys. It's a timer. I wanna run the timer and when it's end do something else(like a warning),and then run again with other amount of minutes. But I can't cause always only the second call is executed:
$(document).ready(function() {
timer(5,timer(25));
// timer(5);
// timer(25); do not work... only exec de last one
});
function timer(countTo,callback){
var time = 10; /* how long the timer runs for */
var initialOffset = '440';
var i = 1
var interval = setInterval(function() {
$('.circle_animation').css('stroke-dashoffset', initialOffset-(i*(initialOffset/countTo)));
$('h2').text(i);
if (i == countTo) {
clearInterval(interval);
}
i++;
}, 1000);
callback();
}
Which is the best solution? There is something that I am not understanding... Thanks anyway!
Well, first off:
timer(5,timer(25));
If you think this line will execute timer(5), and then at the end of timer(5) it will execute timer(25), you are mistaken. This is actually going to evaluate timer(25) immediately, and pass its return value (undefined) as the second parameter to timer(5,undefined).
If you intended to pass that as a callback, you need to pass a function. So you could do:
timer(5,timer.bind(null,25));
But, for that matter, you don't even check if callback exists before attempting to invoke it, so you probably are getting a reference error anyway.
timer(5,timer(25));
starts two timers and passes the result of the second (undefined) to the first as callback. You want:
timer(5,timer.bind(window,25));
And the callback needs to be executed if i==countTo ...
Is this what you want?
timer(5,function(){timer(25)});
Your problem is here:
timer(5,timer(25));
You should type
timer(5, function(){
timer(25)
});
//or using ES6 syntax
timer(5, () => timer(25));
because timer(25) returns its value (this function doesn't return value so it tries to invoke undefined), not that function.
Also read about closures, it might be helpful.
Instead of runing a callback(), you need to run the function itself (timer()). You'll also need to run a for loop inside your function that checks how many times the function has already run. If it reaches your desired maximum, break out of that. This way it won't run indefinitely.
In the following example, the timer() function executes five times, which is what I'm assuming you want by calling timer(5).
$(document).ready(function() {
timer(5);
});
function timer(countTo) {
for (var iterations = 0; iterations < countTo; iterations++) {
var time = 10; /* how long the timer runs for */
var initialOffset = '440';
var i = 1
var interval = setInterval(function() {
$('.circle_animation').css('stroke-dashoffset', initialOffset - (i * (initialOffset / countTo)));
$('h2').text(i);
if (i == countTo) {
clearInterval(interval);
}
}, 1000);
timer();
console.log("Iteration:", iterations + 1);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
To run the function 25 times after this, all you have to do is call timer(25) directly after timer(5):
$(document).ready(function() {
timer(5);
timer(25);
});
$(document).ready(function() {
timer(5);
timer(25);
});
function timer(countTo) {
for (var iterations = 0; iterations < countTo; iterations++) {
var time = 10; /* how long the timer runs for */
var initialOffset = '440';
var i = 1
var interval = setInterval(function() {
$('.circle_animation').css('stroke-dashoffset', initialOffset - (i * (initialOffset / countTo)));
$('h2').text(i);
if (i == countTo) {
clearInterval(interval);
}
}, 1000);
timer();
console.log("Iteration:", iterations + 1);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Hope this helps! :)
I am trying to break the setTimeout function which is started on page load. So what I am doing here is, If I click on the button then I making flag value to true and setTimeout should break which isn't happening here.
This setTimeout function is inside the for each loop. Below is my code.
rData[0].dt.forEach(function(d, i) {
setTimeout(function() {
if(flag === "false"){
console.log(flag);
reserRadius(i); //here I am changing radius of circle
}else{
console.log(flag);
clearTimeout();
return;
}
}, i * 2000);
});
Instead of creating all timeouts in one go, only create them when needed. This way you don't have to clear any of them when you have determined to stop:
(function repeat(list, i) {
if (i >= list.length) return; // nothing (more) to do
var d = list[i]; // do you need d at all??
setTimeout(function() {
if(flag === "false"){
console.log(flag);
reserRadius(i); //here I am changing radius of circle
repeat(list, i+1); // schedule next timeout only now.
}else{
console.log(flag);
// Don't schedule next timeout. This breaks the "loop".
}
}, 2000); // trigger 2 seconds from now. Note: no multiplying anymore.
})(rData[0].dt, 0); // pass initial values: the array and index.
In your version of the code, you would have to keep the id values returned by all setTimeout calls, and then pass them all (or at the least the remaining ones) to clearTimeout, one by one. This would make your code quite cumbersome. I think the above is a more efficient approach.
setTimeout cannot be stopped from its callback itself. setTimeout
returns a timeoutId which can be passed to clearTimeout which in turn will
stop that particualr timer.
One way to stop all such timers is to create an array of timeoutIds and make changes as following.
var timerIds = [];
rData[0].dt.forEach(function(d, i) {
timerIds.push(setTimeout(function(){
if(flag === "false"){
console.log(flag);
reserRadius(i); //here I am changing radius of circle
}
else{
console.log(flag);
}
}, i * 2000));
});
function stopTimeouts(){
timerIds.forEach(function(id){
clearTimeout(id);
}
}
function codeThatMightChangeFlag(callback) {
// do a bunch of stuff
if (condition happens to change flag value) {
// call the callback to notify other code
stopTimeouts();
}
}
Refer: Clear array of setTimeout's and Javascript - wait until flag=true
I haven't done much with Javascript functions and am trying to get a function to loop itself after a short delay. I've seem some code using setTimeout() but I can't seem to figure out how to get it to work in my instance, here's my code:
var number = 8;
var iter = 1;
function slide_function(iter, amount, callback){
if(iter<amount){
console.log('test');
$('#tagline_slideshow span#' + iter).delay(delayTime).fadeOut(1600, function(){
$(this).next('span').fadeIn();
});
var iter = iter + 1;
}
else if(iter==amount){
console.log('test2');
var iter = 1;
$('#tagline_slideshow span#' + iter).delay(delayTime).fadeOut(1600, function(){
$(this).next('span').fadeIn();
});
var iter = iter + 1;
}
}
slide_function(iter, number, setTimeout(slide_function(iter, number),5000));
I'm trying to basically create a little slideshow type thing where elements fade in and out up to a certain point and then start over again. It works the first time through the function but then doesn't run it again. I did make some mistakes where it caused an infinite loop where it pretty much crashed my browser so I'm treading lightly at this point.
I'm assuming the issue lies in my callback function? I'm trying to do a 5000 ms delay before the function runs again.
Any help would be greatly appreciated. Thanks!
This maybe works for you, add this at the end replacing the last line:
setInterval(function(){
slide_function(iter,number)
},5000);
And remove the callback argument in slide_function.
EDIT: Checkout the John T's comment below
I suggest that you do setTimeout after the fadeIn has completed. Otherwise you will run into issues of trying to fadein and fadeout at the same time if your delay is shorter than your fadeIn.
Try rearranging your code to be like this:
// starts at 1, ends at 8
slide_function(1, 8);
function slide_function(iter, amount) {
// defines a function called runAgain inside slide_function()
var runAgain = function() {
// setTimeout takes in a function as the first parameter and delay as the second one
setTimeout(function() {
// inside the function, run slide function with an incremented iter
slide_function(iter+1, amount);
}, 5000);
}
if(iter<amount){
console.log('test');
$('#tagline_slideshow span#' + iter).delay(delayTime).fadeOut(1600, function(){
$(this).next('span').fadeIn(runAgain);
});
}
else if(iter==amount){
console.log('reached the limit, resetting iter to 1');
iter = 1;
$('#tagline_slideshow span#' + iter).delay(delayTime).fadeOut(1600, function(){
$(this).next('span').fadeIn(runAgain);
});
}
}
Sirikon's answer works, an alternative is to chain your calls from within slide_function, also, this code avoid globals. Not tested, so it probably has a few bugs.
function slideShow(iter, amout) {
function slide_function(){
// Using mod operations is the common way to handle looping around the end
iter = iter % amount;
$('#tagline_slideshow span#' + iter).delay(delayTime).fadeOut(1600, function(){
$(this).next('span').fadeIn();
});
// You were using var, so you were creating a new variable instead
// of modifying the shared variable
iter++;
}
setTimeout(slideShow, 5000);
}
slideShow(0,8);