issues with clearInterval - javascript

I cannot seem to get clearInterval to work in the manner that I believe it should. I have been going through so many other examples and questions. In each case I seem to be doing what everyone else is doing, excepting the use of onkeypress. I have also tried various other keypress options with no luck. And I have also flipped around the code a bunch of different times to no avail.
Below is the basic pieces of code I have been working with, much simplified from the larger project. As you might be able to discern, I want it to keep printing "poop " until a keypress stops it. If I hit a button before it prints "poop" the first time, it will stop and print "stopped pooping." If I wait until after it has printed "poop" once, I cannot get it to stop with a keypress.
Please excuse the scatological nature of it.
function pooping() {
document.write("poop ");
}
var pooper = setInterval(pooping, 1000);
document.onkeypress = function() {
clearInterval(pooper);
pooper = 0;
document.write("stopped pooping");
}

Same code, without the document.write() works fine:
'use strict';
let target = document.getElementById('target');
function pooping() {
// Normally would use console.log() here, but wanted the result visible
// in the snippet.
target.textContent += "poop ";
}
var pooper = setInterval(pooping, 1000);
document.onkeypress = function() {
clearInterval(pooper);
pooper = 0;
target.textContent += "stopped pooping";
}
<div id="target"></div>

Related

Cannot force DOM redraw using window.getComputedStyle or element.offsetHeight?

I have a function that takes a little while to run, so I'm trying to set the display value of a div (that basically says "please wait") to "block" at the start of the function and then to "none" when the function is complete to alert the user that the function is running.
The display setting is being set properly and I can see the changes in the console log; however, I cannot get it to redraw until after the function is complete. After hours of scouring forum answers to similar questions, it seems like my best option is to call window.getComputedStyle or element.offsetHeight, but these don't seem to be working.
Here is my code:
function renumber(){
notif = document.getElementByID("notification"); //this is my div
notif.style.display = "block";
window.getComputedStyle(notif, null).height; //I've tried this as well as...
notif.offsetHeight; //trying this seperately
/////////big loop that takes a long time to run/////////
notif.style.display = "none";
}
I have also tried setting the CSS instead, and then using window.getComputedStyle and element.offsetHeight:
doc = document.styleSheets[6].cssRules[0].style;
disp = doc.setProperty('display','block');
window.getComputedStyle(notif, null).height; //I've tried this as well as...
notif.offsetHeight; //trying this seperately
And, I have even tried abandoning my notification div and using setTimeout() and alert(), but even that doesn't display until the function is complete (it doesn't matter how much time I put in):
function renumber(){
setTimeout(function() { alert("my message"); }, 50);
////big loop that takes a long time to run////
}
So with all of that, my question is why is window.getComputedStyle and element.offsetHeight not redrawing and making my div display the way the values are set within the function? Everything I've seen online sounds like simply calling one of those should force a redraw, so I'm confused why I'm not seeing a redraw. Is there a better way I should be doing this?
Would tell problem is JS is single threaded, you block UI by your slow operation.
Do not know if there is a process events signal to UI common in frameworks like Qt, etc., but setTimeout can help here (may not work perfect here in snippet, slow part takes around 6s in my Chrome):
function renumber(){
notif = document.getElementById("notification"); //this is my div
notif.style.display = "block";
setTimeout(long, 0, notif);
}
renumber();
function long(notif) {
console.log("Long starts", new Date());
for(var a=0;a<1000000000;a++) if(a % 10) a++; else a += 0.1;
console.log("Long end", new Date());
notif.style.display = "none";
}
<div id="notification" style="display: none;">
Wait please !
</div>

How to force HTML page to update in a for loop?

This might be a simple question but I have been searching for an answer to no avail. The code below is supposed to read five strings then update the page with these values, one value at a time. However, this worked only in Firefox (i.e. the document is updated after each iteration). Other browsers just showed the last value!! Anyone can help me understand this weird behavior?
<h1 id="test"></h1>
<script>
for(var i = 0; i<5; i++){
var x = window.prompt("Write something..");
document.getElementById("test").innerHTML = x;
}
</script>
Edit: Thanks everyone. setTimeout worked fine. what is the best alternative to window.prompt? Forms?
You have to give a chance for browser to do its job. When you are in a loop, you are occupying browser. Following example works in chrome:
<h1 id="test"></h1>
<script>
function askOne() {
var x = window.prompt("Write something..");
document.getElementById("test").innerHTML = x;
}
function ask() {
for(var i = 0; i<5; i++){
setTimeout(askOne, 0);
}
}
setTimeout(ask, 0);
</script>
With setTimeout(), you are giving a change to browser to do somethings. Which it may or may not use.
While it might be ok for some local test.html, in real life, one does not do it this way.
The old school way is to use setInterval or setTimeout.
function updatePage(x) {
// perform some change to the DOM, e.g.
document.getElementById("test").innerHTML = x;
}
// This will call updatePage(x) every 5000ms
setInterval(() => {updatePage(x)}, 5000);
Pretty sure this is deprecated though. And what you probably want to do is bind an event listener to the input element that x comes from.

Javascript/AJAX Asynchronous Loading Spinners

I am trying to make a basic enough page that allows the user to execute a php script by clicking a button. Each button will have a loading spinner popup on clicking.
My problem is, on clicking one button and then clicking another, both spinners close at the exact same time even though the second may still be processing.
Does anyone know how to make these spinners truly asynchronous ? Thanks so much in advance, its killing me.
JS:
function test(element){
var append = "#";
var test = append.concat(element);
document.getElementById(element).style.visibility='visible';
$.ajax({url:"test.php",success:function(result){
hide(element);
}
});
};
function hide(element){
document.getElementById(element).style.visibility='hidden';
};
</script>
HTML:
<html>
<?
$index = 0;
$myArray = array ("1", "2", "3", "4", "5");
for($index = 0; $index < 5; $index++){?>
<button onclick="test('<?echo $myArray [$index];?>')">Start</button>
<img id="<?echo $myArray [$index];?>" src="images/loader.gif"
style="visibility:hidden"/>
<br><br>
<?}?>
</html>
I would implement a counter. Each time you show the loading indicator, add one to the counter and each time you want to hide it, subtract one. Then monitor the counter and whenever it is above zero show the loading indicator and when at zero hide it. Make sense?
Something like the following (untested) code might do the trick and it neatly means you can avoid worrying about the spinner at all in ajax requests:
var spinningAjax = (function() { // use of the closure created by an immediate function gives us the scope to create a persistant counter variable
var counter = 0;
$(document).ajaxComplete(function() {
counter--;
if (counter === 0) {
showSpinner(false);
}
});
return function(settings) {
counter++;
showSpinner(true);
$.ajax(settings);
}
})();
var showSpinner(bool) {
// I'll leave this up to you as it looks like your posted html / js is for example purposes rather than replicating your actual site
};
EDIT: Ok, having seen the comments to another answer, I realise this doesn't quite solve the situation you're in. I'll have a think and see if I can do better
EDIT2: I think this (still untested, unfortunately) code may be what you require. Please let me know in the comments if you have any issues.
var spinningAjax = (function() { // closure of immediate function lets us create a persistant array of the counters for each spinner
var counter = []; // an array to hold the counters for each spinner
$(document).ajaxComplete(function(event, xhr, settings) { // called whenever any ajax request is completed
if (typeof settings.ajaxGroup !== 'undefined') { // only update the counters if an ajaxGroup has been provided
counter[settings.ajaxGroup]--;
if (counter[settings.ajaxGroup] === 0) {
showSpinner(false, settings.ajaxGroup); // hide spinner when all requests connected with the spinner have been completed
}
}
});
return function(settings) { // this is the function actually assigned to the variable spinningAjax as a result of the immediate function
counter[settings.ajaxGroup] = counter[settings.ajaxGroup] ? counter[settings.ajaxGroup]+1 : 1; // can't just use the ++ operator as this property might not be defined yet
showSpinner(true, settings.ajaxGroup);
$.ajax(settings);
}
})();
var showSpinner(bool, spinnerIdentifier) {
// I'll leave this up to you as it looks like your posted html / js is for example purposes rather than replicating your actual site
};

JavaScript two onload functions

I am quite new to JavaScript programming and I'm trying to create some scripts that would save me time in maintaining one of my websites.
Now I have two functions in the same script that I'm calling from the head of my document and I'm trying to get them both to load at the same time with an onload event handler. I am doing that with window.onload command in my script (I want to make my script as unobtrusive as possible so I'm just calling the script in the header).
The problem is that only the first function loads and the second one doesn't. Can both functions be called with:
window.onload=function(){
function1();
function2();
}
or is there a different code I need to use to accomplish this?
I would really appreciate it if you could make your explanations as simple as possible as I am very new to JavaScript and programming in general.
P.S. If more than one function can't be loaded with onload, could you please explain to me why this is the case so I know in the future.
Ok, I see by the answers that my question probably left too much for assumption so here is the entire code of the functions I am trying to call (this is the script I am calling in the head of my html document):
I was trying to avoid putting the code here because my variables are written in Serbian language (as I am from Serbia), but I hope that you will still be able to look through it without much confusion.
In the code below I am calling at the bottom of the script two functions (lista() and ostale()) and the moveover() function is just a helper function called by the lista() function.
In essence the first one (lista()) lists through all elements of div "boje" (in English translated to "colors") and depending on the color the user hovers their mouse over, the background image changes. It also adds a few attributes to those image elements that the user is supposed to hover over.
The second one (ostale() (Translated to English "others") is supposed to only add attributes to the rest of the color images that are not supposed to do anything if the user hovers over them.
But when I open the page in localhost it doesn't show in Firefox's inspect element that any attributes have been added to the images within the div "ostale".
function lista()
{
var boje = document.getElementById('boje');
var broj = boje.childNodes.length;
for(i=1; i<broj; i++)
{
var stavka = boje.childNodes.item(i);
stavka.setAttribute("id", i);
stavka.setAttribute("onmouseover", "moveover(src)");
stavka.setAttribute("alt", "Boja");
stavka.setAttribute("class", "boja");
stavka.hspace="2";
stavka.height="23";
}
}
function moveover(adresaBoje)
{
var izvor = adresaBoje;
var slika = izvor.slice(0, izvor.length-4);
var pocetak = "url(";
var ekstenzija = ".jpg)";
var novaSlika = pocetak.concat(slika, ekstenzija);
document.getElementById('slika').style.backgroundImage=novaSlika;
}
function ostalo(){
var ostaleboje = document.getElementById('ostale');
var ostalebroj = ostaleboje.childNodes.length;
for(n=1; n<ostalebroj; n++)
{
var ostalestavka = ostaleboje.childNodes.item(n);
ostalestavka.setAttribute("alt", "Boja");
ostalestavka.hspace="2";
ostalestavka.height="23";
}
}
window.onload=function(){
try
{
lista();
ostalo();
}
catch(err)
{
alert(err);
}
}
After I try to load the page it alerts me with an error: "TypeError: stavka.setAttribute is not a function".
This is the html document I am trying to manipulate:
<div id="slika" style="background-image: url(images/nova_brilliant/1.jpg)">
</div>
<div id="tekst">
<h1>Nova Brilliant</h1>
<div id="sadrzaj">
<p>Pređite mišem preko željene boje da biste videli kako izgleda ova kuhinja u toj boji:</p>
<div id="boje">
<img src="images/nova_brilliant/1.gif"><img src="images/nova_brilliant/2.gif"><img src="images/nova_brilliant/3.gif">
</div>
<p>Ostale dostupne boje:</p>
<div id="ostale">
<img src="images/nova_brilliant/4.gif"><img src="images/nova_brilliant/5.gif"><img src="images/nova_brilliant/6.gif">
</div>
</div>
</div>
I ran into the same problem. I came across a couple of help but this one was easy to understand and it worked for me:
window.addEventListener("load", func1); window.addEventListener("load", func2);
just like #Quentin You can read more about it here
Yes you can. However, if the first goes wrong, the second won't fire.
Use this to catch errors:
try { //try executing the functions
function1();
function2();
}
catch(error) { // If there's an error
alert(error); // alert the error.
}
It is a good practice to put try and catch when experimenting with javascript.
Edited: Sorry i confused childNodes[] with childNodes.item().
By the way, I tried something like this, and it works just fine:
<head>
<script>
window.onload = function() {
div = document.getElementById("someDiv");
length = div.childNodes.length;
first();
second();
}
function first() {
for(var i=0;i<length;i++) {
var set = div.childNodes.item(i);
set.setAttribute("name", "span " + (i+1));
}
}
function second() {
for(var i=0;i<length;i++) {
name = div.childNodes[i].getAttribute("name");
console.log(name);
}
}
</script>
</head>
<body>
<div id='someDiv'><span id='span1'></span><span id='span2'></span></div>
</body>
UPDATE: I found the error:
Actually there's nothing wrong with your code. It works just fine, however, the last item of boje is empty space, which means, a text node. That's why the error keeps showing up. Change for(i=1; i<broj; i++) with for(i=1; i<broj-1; i++) and everything should be good.
Can both functions be called with
Yes. If you add event handlers by assigning to DOM properties, then you can only assign a single function to each but that function can call other functions.
However, if you do that and the first function throws an error then the second function won't fire at all. It will also discard the context and arguments, as they won't be passed to the called functions.
You could work around those problems like so:
window.onload=function(){
try {
function1.apply(this, arguments);
} catch (e) { }
try {
function2.apply(this, arguments);
} catch (e) { }
}
or is there a different code I need to use to accomplish this?
You should use addEventListener instead. That avoids the need to fiddle with apply, and protects you from errors being thrown. See the MDN events documentation for more details.
window.addEventListener('load', function1);
window.addEventListener('load', function2);

Problem with setInterval

I have a problem with setInterval on a 'click' event. Im already spawning a hidden div. But I want spawn and fade in synced and only run the fade in ones.
var anchor = document.getElementById('anchor');
var hiddenElement = document.getElementById('hiddenElement');
var bkgFade = document.getElementById('bkgFade');
anchor.addEventListener('click', spawnImage, false);
hiddenElement.addEventListener('click', despawnImage, false);
function spawnImage() {
setInterval(function() {
document.getElementById('hiddenElement').style.display = 'block';
document.getElementById('bkgFade').style.visibility = 'visible';
hiddenElement.style.opacity += 1.0;
} 1000);
}
function despawnImage() {
document.getElementById('hiddenElement').style.display = 'none';
document.getElementById('bkgFade').style.visibility = 'hidden';
}
This piece of code makes no sense to me:
function spawnImage() {
setInterval(function() {
document.getElementById('hiddenElement').style.display = 'block';
document.getElementById('bkgFade').style.visibility = 'visible';
F hiddenElement.style.opacity += 1;
});
clearInterval();
}
There are multiple things wrong with it.
You aren't setting a time for setInterval().
You aren't saving the return value from setInterval().
You aren't passing anything to clearInterval(). It should be passed the return value from setInterval(). It won't do anything the way you have it.
This line isn't legal javascript: F hiddenElement.style.opacity += 1;
I can't understand why you would setInterval() and then immediately clearInterval()
Adding 1 to the opacity in a timer interval isn't going to accomplish anything. That's just going to make it visible in one giant step.
Manipulating opacity directly won't work in some versions of IE.
You're using document.getElementById('hiddenElement') in one place and hiddenElement in another place. If hiddenElement is valid, use that both places. If not, use document.getElementById('hiddenElement') both places.
If you describe in more detail what you're really trying to accomplish with this code, folks might be able to help you come up with correct code.
I would highly recommend that animation be done with a library like jQuery or YUI as it's way, way easier to use and make it work cross-browser.

Categories

Resources