Javascript code not running sequentially - javascript

Halcyon helped with the loop here - but is there any way to stop the subsequent code from running, until after the loop has finished:
When I run the code below, I get the prompt for entering my name, before the scrollDown function runs - but the input prompt as AFTER the scrollDown code:
function scrollDown(num_times) {
num_times -= 1;
if (num_times === 0) {
return;
}
window.scrollBy(0, 500); // horizontal and vertical scroll increments
setTimeout(function() {
scrollDown(num_times);
}, 500);
}
//This should run first and scroll the screen before prompting
scrollDown(30); // scroll down 30 times
//However this prompt comes up before the above code has ran
var kw = prompt("Please enter your name");
Any advice would be appreciated.
Thanks, Mark

Put the code in a callback that you run after the last scroll iteration.
function scrollDown(num_times, callback) {
if (num_times === 0) {
callback();
}
window.scrollBy(0, 500); // horizontal and vertical scroll increments
setTimeout(function() {
scrollDown(num_times - 1, callback);
}, 500);
}
//This should run first and scroll the screen before prompting
scrollDown(30, function() {
kw = prompt("Please enter your name");
document.getElementById("result").textContent = kw;
}
); // scroll down 30 times
Your name is: <span id="result"></span>

var kw;
function scrollDown(num_times) {
if (num_times === 0) {
/* you can define your prompt after scrollDown has ended */
kw = prompt("Please enter your name");
return;
}
window.scrollBy(0, 500);
setTimeout(function() {
scrollDown(num_times - 1);
}, 500);
}
scrollDown(30);

Related

Propagation issue with jQuery button

I have created a javascript / jquery simple game actioned by two buttons.
Button 1: "Play". The game is played ten times, and increments using a variable that I store in local storage. If the increment matches a random number between one and 10, then console.log("match!"), otherwise console,log("no match");
Button 2: "Restart the game". Once the increment is bigger than 10, then the game starts over by clicking the button.
The problem is, if you look into the console, that the second time you play the game, the game jumps by two increments at a time. The third time, by three increments... Why is that? There is a propagation issue here. How do I solve it?
Jsfiddle is here. Code I paste below, as it's very small:
start_game_over()
function start_game_over(event) {
$("#button1").show();
$("#button2").hide();
localStorage.setItem("progress", 0)
$("#button1").on("click", function() {
if (parseInt(localStorage.getItem("progress")) < 11) {
var random_number = Math.floor(Math.random() * (10 - 0) + 0);
console.log(parseInt(localStorage.getItem("progress")));
if (parseInt(localStorage.getItem("progress")) === random_number) {
console.log("match!")
} else {
console.log("no match")
}
localStorage.setItem("progress", parseInt(localStorage.getItem("progress")) + 1);
} else {
$("#button1").hide();
$("#button2").show();
$("#button2").on("click", function() {
start_game_over();
});
}
})
}
When you start_game_over you assign an event listener to the button click, but you never remove it, so when you call the function again it attaches a new one. restructuring the code a bit to handle the clicks outside of that function would be a good idea.
function start_game_over(event) {
$("#button1").show();
$("#button2").hide();
localStorage.setItem("progress", 0)
}
$("#button1").on("click", function() {
if (parseInt(localStorage.getItem("progress")) < 11) {
var random_number = Math.floor(Math.random() * (10 - 0) + 0);
console.log(parseInt(localStorage.getItem("progress")));
if (parseInt(localStorage.getItem("progress")) === random_number) {
console.log("match!")
} else {
console.log("no match")
}
localStorage.setItem("progress", parseInt(localStorage.getItem("progress")) + 1);
} else {
$("#button1").hide();
$("#button2").show();
}
})
$("#button2").on("click", function() {
start_game_over();
});
You are declaring your event listeners inside a function, so every time the function is called, you are creating a new listener. Put them out of the function and the problem is fixed.
You have to clear your $('#button1') event at the end by using $('#button1').off('click');. This will clear the event listener you create every time in your function.
SE doesn't allow localStorage so run this on JSFiddle or local machine.
start_game_over()
function start_game_over(event) {
$("#button1").show();
$("#button2").hide();
localStorage.setItem("progress", 0)
$("#button1").on("click", function() {
if (parseInt(localStorage.getItem("progress")) < 11) {
var random_number = Math.floor(Math.random() * (10 - 0) + 0);
console.log(parseInt(localStorage.getItem("progress")));
if (parseInt(localStorage.getItem("progress")) === random_number) {
console.log("match!")
} else {
console.log("no match")
}
localStorage.setItem("progress", parseInt(localStorage.getItem("progress")) + 1);
} else {
$("#button1").hide();
$("#button2").show();
$("#button2").on("click", function() {
$("#button1").off("click");
$("#button2").off("click");
start_game_over();
});
}
})
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="button1">Play</button>
<button style="display:none" id="button2">Restart the Game</button>
The problem is that each time you invoke start_game_over you are adding additional click handlers to #button1 and #button2. What you want to do is set up the click handlers once and use a function to reset your game state when #button2 is clicked (or just reset the game state in #button2's callback). For example:
var $button1 = $('#button1');
var $button2 = $('#button2');
var start_game_over = function () {
$button1.show();
$button2.hide();
localStorage.setItem('progress', 0)
};
$button1.on('click', function () {
var progress = parseInt(localStorage.getItem('progress'), 10 /*always include radix!*/);
if (progress < 11) {
//... game logic here
} else {
$button1.hide();
$button2.show();
}
});
$button2.on('click', function () {
start_game_over();
});

jQuery fade in/out between divs

I have this code:
var next = null;
var outer = jQuery('.banner .container');
var current = outer.find('.banner-word:first');
current.fadeIn();
function fade() {
if (current.next('div.banner-word').length > 0) {
next = current.next('div.banner-word');
} else {
next = outer.find('.banner-word:first');
}
current.fadeOut();
next.fadeIn();
current = next;
setTimeout(fade, 11000);
}
// start the process
fade();
A few problems with it - 1) It seems to ignore the first banner-word div 2) On load it shows quickly shows the first 2 banner-word divs and then starts with the second banner-word div
Am I missing something obvious?
Try changing:
if (current.next('div.banner-word').length > 0) {
next = current.next('div.banner-word');
} else {
next = outer.find('.banner-word:first');
}
to:
if (current.next().is('div.banner-word')) {
next = current.next();
} else {
next = outer.find('.banner-word:first');
}
Edit:
try adding a delay to the initial fade() call.
Change
fade();
to
setTimeout(fade, 5000);

Stop notification timeout on hover

I have a slider error notification which slides out, displays error and disappears in 10 seconds. It can be removed earlier if you click on it.
On thing that is missing is stopping it disappearing by mouse hover.
For some people, 10 seconds is not enough to read the error.
So I need to stop the timer with mouse hover.
To summarise: I'd like it to disappear in 10 seconds as it is now if you don't interact with it. If I hover over it, the timer should be stopped and the message should stay there till you click on it to remove.
How can I stop the timer (the setTimeout function) on hover?
This is the message slider function:
function popa(text = "Internal Error!", type = "error") {
var alert = document.createElement("div");
var stack = document.querySelectorAll('.alert').length;
alert.appendChild(document.createTextNode(`${stack + 1}: ${text}`));
alert.classList.add("alert");
alert.classList.add(`alert-${type}`);
document.querySelector("body").appendChild(alert);
var steps = 20;
var width = parseInt(getComputedStyle(alert).getPropertyValue('width'));
var slide = width + Math.round(width / 4);
var step = Math.round(slide / steps);
alert.onclick = () => {
alert.parentNode.removeChild(alert);
};
(function next(cnt, max) {
if (cnt++ > max) {
return;
}
alert.style.right = `${(-width) + (step * cnt)}px`;
setTimeout(() => {
next(cnt, max);
}, 10);
})(0, steps);
console.log(text);
setTimeout(() => {
if (alert.parentNode) {
alert.parentNode.removeChild(alert);
}
}, 10000);
}
Thank you.
P.S. No jQuery please
window.setTimeout() returns an integer timerID which you can use with window.clearTimeout():
var notificationTimer;
function createTimer() {
notificationTimer = setTimeout(function(){ alert('hi'); }, 10000);
}
function clearTimer() {
clearTimeout(notificationTimer);
}
See also: https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout

Autoscroll with JavaScript console in browser

I know the scrollBy functions but is it possible to scroll down a web page with a command typed in the JavaScript console, so that the page automatically scrolls with the passed parameters?
Typing the function
function pageScroll() {
window.scrollBy(0,50); // horizontal and vertical scroll increments
scrolldelay = setTimeout('pageScroll()',100); // scrolls every 100 milliseconds
}
and then calling it does nothing in Chrome.
Give this a try; I use it myself often.
(function() {
var intervalObj = null;
var retry = 0;
var clickHandler = function() {
console.log("Clicked; stopping autoscroll");
clearInterval(intervalObj);
document.body.removeEventListener("click", clickHandler);
}
function scrollDown() {
var scrollHeight = document.body.scrollHeight,
scrollTop = document.body.scrollTop,
innerHeight = window.innerHeight,
difference = (scrollHeight - scrollTop) - innerHeight
if (difference > 0) {
window.scrollBy(0, difference);
if (retry > 0) {
retry = 0;
}
console.log("scrolling down more");
} else {
if (retry >= 3) {
console.log("reached bottom of page; stopping");
clearInterval(intervalObj);
document.body.removeEventListener("click", clickHandler);
} else {
console.log("[apparenty] hit bottom of page; retrying: " + (retry + 1));
retry++;
}
}
}
document.body.addEventListener("click", clickHandler);
intervalObj = setInterval(scrollDown, 1000);
})()
It might show you an error "too much recursion"
You should try setInterval() instead of setTimeout(). Check this sample code for that.
setInterval(function(){
window.scrollBy(0,50);
},100);

Autostart jQuery slider

I'm using a script that animates on click left or right to the next div. It currently works fine but I'm looking to add two features to it. I need it to repeat back to the first slide if it is clicked passed the last slide and go to the last slide if click back from the first slide. Also, I'm interested in getting this to autostart on page load.
I've tried wrapping the clicks in a function and setting a setTimeout but it didn't seem to work. The animation is currently using CSS.
Here's the current JS:
<script>
jQuery(document).ready(function() {
var boxes = jQuery(".box").get(),
current = 0;
jQuery('.right').click(function () {
if (current == (-boxes.length + 1)){
} else {
current--;
updateBoxes();
}
console.log(-boxes.length + 1);
console.log(current);
});
jQuery('.left').click(function () {
if (current === 0){
} else{
current++;
updateBoxes();
}
});
function updateBoxes() {
for (var i = current; i < (boxes.length + current); i++) {
boxes[i - current].style.left = (i * 100 + 50) + "%";
}
}
});
</script>
Let me know if I need a jsfiddle for a better representation. So far, I think the code is pretty straightforward to animate on click.
Thanks.
Try
jQuery(document).ready(function () {
var boxes = jQuery(".box").get(),
current = 0,
timer;
jQuery('.right').click(function () {
if (current == (-boxes.length + 1)) {
current = 0;
} else {
current--;
}
updateBoxes();
}).click(); //initialize the view
jQuery('.left').click(function () {
if (current === 0) {
current = -boxes.length + 1;
} else {
current++;
}
updateBoxes();
});
function updateBoxes() {
//custom implementation for testing
console.log('show', current)
$(boxes).hide().eq(-current).show();
autoPlay();
}
function autoPlay() {
clearTimeout(timer);
//auto play
timer = setTimeout(function () {
jQuery('.right').click();
}, 2500)
}
});
Demo: Fiddle
Here's an example based on my comment (mostly pseudocode):
$(function(){
var boxes = $('.box'),
current = 0,
timer;
// Handler responsible for animation, either from clicking or Interval
function animation(direction){
if (direction === 1) {
// Set animation properties to animate forward
} else {
// Set animation properties to animate backwards
}
if (current === 0 || current === boxes.length) {
// Adjust for first/last
}
// Handle animation here
}
// Sets/Clears interval
// Useful if you want to reset the timer when a user clicks forward/back (or "pause")
function setAutoSlider(set, duration) {
var dur = duration || 2000;
if (set === 1) {
timer = setInterval(function(){
animation(1);
}, dur);
} else {
clearInterval(timer)
}
}
// Bind click events on arrows
// We use jQuery's event binding to pass the data 0 or 1 to our handler
$('.right').on('click', 1, function(e){animation(e.data)});
$('.left').on('click', 0, function(e){animation(e.data)});
// Kick off animated slider
setAutoSlider(1, 2000);
Have fun! If you have any questions, feel free to ask!

Categories