I came across a situation were a button event handler calls a function, that may take a couple seconds to complete depending on the input. Once the function completes, the output will show up in a grid.
The function is completely client side. Right before the function is running I add a css class to the grid wrapper div that basically just shows a 'loading' gif/animation.
This works fine in Chrome, but not in Firefox and IE 11.
Below is an oversimplified version of how I achieve this with javascript setTimeout 0.
$('#calc').on('click', function(){
$('#container').addClass('loading');
calculate(10, function(res){
$('#result').text(res);
$('#container').removeClass('loading');
});
});
//represents my long running function
function fib(n) {
if(n<2) {
return n;
}
return fib(n-2) + fib(n-1);
}
//will be called by click handler
function calculate(n,cb) {
setTimeout(function(){
var result = fib(n);
return cb(result);
},0)
}
As you can see I use setTimeout 0 in calculate(n,cb) to give the browser the ability to show the 'loading' animation before the function starts and then remove it when it is done.
However, this does not work in Firefox.
What are some other options for me to achieve what I am trying to do?
I am using jQuery here, but the actual project I am working on is using Angular5. But the idea should be the same.
Here is a jsFiddle to show what I am trying to do. Using the loading animation by Mattln4D (thanks)
https://jsfiddle.net/alabianc/qL5zggh7/
If you want to see some actual good result, run it with 40 as an input in calculate, but no more than that!
I appreciate any help!
I think if you set the timeout = 0, it is so fast to browser can show the loading animation. When I try to change timeout = 10 or 100, I can see the loading animation show on both of chrome, ff.
Related
So I got a little codepen. Everything works so far except a little thing. I got a <h1> and an <input>. When I type something in the text input, its value should get passed to the <h1> in realtime.
I tried to do that with a keyup function:
$('input[name=titleInput]').keyup(function(){
$('#title').text(this.value);
});
Something happens, but not what I want.When I type something in the text input, then delete it (with backspace) and re-enter something, only the first character gets passed to the title.Try it out on my codepen. Maybe it's just a stupid mistake of mine, but to me this behaviour is pretty weird.Thanks for your help in advance!EDIT:I am using text-fill-color, which may causes the problem.EDIT 2:A friend of mine tested it. It worked for her. She's using Chrome and the same version as me (58.0.3029.110 (official build) (64-Bit)).
Chrome does not update the content correctly. Such kind of bugs can always happen if you use vendor prefixed css properties, so you should avoid those.
You could hide the container before update, and then show it again with a timeout. This will trigger an update, but would also result in flickering.
$('input[name=titleInput]').keyup(function(){
$('.clipped').hide()
$('#title').text(this.value);
setTimeout(function() {
$('.clipped').show();
})
});
EDIT An alternative might be to use background-clip on the text and provide the inverted image yourself, but I right now don't have time to test that.
EDIT2 Based on the test of #TobiasGlaus the following code does solve the problem without flickering:
$('input[name=titleInput]').keyup(function(){
$('.clipped').hide().show(0)
$('#title').text(this.value);
});
This seems to be different to $('.clipped').hide().show() most likely it starts an animation with duration 0 and uses requestAnimationFrame which also triggers a redraw. To not relay on this jQuery behaviour, the code should be written as:
$('input[name=titleInput]').keyup(function(){
if( window.requestAnimationFrame ) {
$('.clipped').hide();
}
$('#title').text(this.value);
if( window.requestAnimationFrame ) {
window.requestAnimationFrame(function() {
$('.clipped').show();
})
}
});
i'd use the following lines:
$('input[name=titleInput]').bind('keypress paste', function() {
setTimeout(function() {
var value = $('input[name=titleInput]').val();
$('#title').text(value);
}, 0)
});
This will listen to the paste / keypress events, and will update the value on change.
The title of the question expresses what I think is the ultimate question behind my particular case.
My case:
Inside a click handler, I want to make an image visible (a 'loading' animation) right before a busy function starts. Then I want to make it invisible again after the function has completed.
Instead of what I expected I realize that the image never becomes visible. I guess that this is due to the browser waiting for the handler to end, before it can do any redrawing (I am sure there are good performance reasons for that).
The code (also in this fiddle: http://jsfiddle.net/JLmh4/2/)
html:
<img id="kitty" src="http://placekitten.com/50/50" style="display:none">
<div>click to see the cat </div>
js:
$(document).ready(function(){
$('#enlace').click(function(){
var kitty = $('#kitty');
kitty.css('display','block');
// see: http://unixpapa.com/js/sleep.html
function sleepStupidly(usec)
{
var endtime= new Date().getTime() + usec;
while (new Date().getTime() < endtime)
;
}
// simulates bussy proccess, calling some function...
sleepStupidly(4000);
// when this triggers the img style do refresh!
// but not before
alert('now you do see it');
kitty.css('display','none');
});
});
I have added the alert call right after the sleepStupidly function to show that in that moment of rest, the browser does redraw, but not before. I innocently expected it to redraw right after setting the 'display' to 'block';
For the record, I have also tried appending html tags, or swapping css classes, instead of the image showing and hiding in this code. Same result.
After all my research I think that what I would need is the ability to force the browser to redraw and stop every other thing until then.
Is it possible? Is it possible in a crossbrowser way? Some plugin I wasn't able to find maybe...?
I thought that maybe something like 'jquery css callback' (as in this question: In JQuery, Is it possible to get callback function after setting new css rule?) would do the trick ... but that doesn't exist.
I have also tried to separte the showing, function call and hiding in different handlers for the same event ... but nothing. Also adding a setTimeout to delay the execution of the function (as recommended here: Force DOM refresh in JavaScript).
Thanks and I hope it also helps others.
javier
EDIT (after setting my preferred answer):
Just to further explain why I selected the window.setTimeout strategy.
In my real use case I have realized that in order to give the browser time enough to redraw the page, I had to give it about 1000 milliseconds (much more than the 50 for the fiddle example). This I believe is due to a deeper DOM tree (in fact, unnecessarily deep).
The setTimeout let approach lets you do that.
Use JQuery show and hide callbacks (or other way to display something like fadeIn/fadeOut).
http://jsfiddle.net/JLmh4/3/
$(document).ready(function () {
$('#enlace').click(function () {
var kitty = $('#kitty');
// see: http://unixpapa.com/js/sleep.html
function sleepStupidly(usec) {
var endtime = new Date().getTime() + usec;
while (new Date().getTime() < endtime);
}
kitty.show(function () {
// simulates bussy proccess, calling some function...
sleepStupidly(4000);
// when this triggers the img style do refresh!
// but not before
alert('now you do see it');
kitty.hide();
});
});
});
Use window.setTimeout() with some short unnoticeable delay to run slow function:
$(document).ready(function() {
$('#enlace').click(function() {
showImage();
window.setTimeout(function() {
sleepStupidly(4000);
alert('now you do see it');
hideImage();
}, 50);
});
});
Live demo
To force redraw, you can use offsetHeight or getComputedStyle().
var foo = window.getComputedStyle(el, null);
or
var bar = el.offsetHeight;
"el" being a DOM element
I do not know if this works in your case (as I have not tested it), but when manipulating CSS with JavaScript/jQuery it is sometimes necessary to force redrawing of a specific element to make changes take effect.
This is done by simply requesting a CSS property.
In your case, I would try putting a kitty.position().left; before the function call prior to messing with setTimeout.
What worked for me is setting the following:
$(element).css('display','none');
After that you can do whatever you want, and eventually you want to do:
$(element).css('display','block');
I have made a carousel and using JavaScript setInterval() function for rotate image with fixed interval in carousel. Here's the script that I had used:
var timeOut = 4000;
function showSlide() {
//....script for showing image
}
function pauseSlide() {
setInterval(function(){showSlide();}, timeOut);
}
jQuery(document).ready(function() {
pauseSlide();
});
Now the problem is when I have change the browser tab and after few minute back again to carousel browser and what I seen carousel running too faster rather than default time interval, images going to change fast suppose 0 time interval. Please help me with how I can sort this out.
You must get rid of the first interval before starting another, or you start getting more than one interval working simultaneously (i.e. why you start seeing it go "faster")
Do this
var timeOut = 4000;
var interval = 0;
function showSlide() {
//....script for showing image
}
function pauseSlide() {
clearInterval(interval);
interval = setInterval(function(){showSlide();}, timeOut);
}
jQuery(document).ready(function() {
//NOW you can do multiple pauseSlide() calls
pauseSlide();
pauseSlide();
pauseSlide();
pauseSlide();
pauseSlide();
});
From what I know in newer versions of both firefox and chrome, background tabs have setTimeout and setInterval clamped to 1000ms to improve performance. So I think that your issue might relate to that.
Maybe this will help : How can I make setInterval also work when a tab is inactive in Chrome?
Image changing faster than expected may indicate that you have more than one call to pauseSlide(), in one way or another.
Is document ready the only place you call the function ? Any code in showslide or anywhere triggering a document ready event ? If you put an alert() in pauseSlide(), does it popup more than once ?
I wrote the following simple function that takes two parameters mostly coming from another function returned with json from the server.
var timing = 10000;
function notificationOutput(type, message) {
console.log('output now!');
var note = $('.notification');
note.css('display', 'none');
if ( type == "success" ) { note.removeClass('warning').addClass('success'); }
if ( type == "warning" ) { note.removeClass('success').addClass('warning'); }
note.find('.message').html(message);
note.slideDown( function() {
note.delay(timing).slideUp();
});
}
All it does is simply sliding down a bar from the top of my page putting out a message (either success or warning). The timing variable is for the notification-bar to stay for 10 seconds. So when the function is triggered I want the bar to slideDown(), hold that position for 10seconds and than slideUp() again.
However right now when the function is triggered there is a weird timeout happening till the notification bar appears. That means when the function is fired the console.log() output I have in there right now is logged immediately in my JS-console but the slideDown() takes a few seconds longer to appear! Why is that?
I want the slideDown() to happen immediately (at the same time as the output now is logged in the console). Why is there a delay happening?
Thanks for your help!
Nothing obvious there. I would try trimming the code down until it slides down as expected. Remove the callback, remove the html-set, remove the success/warning class-setters, select the note-element before outputting to the console, replace the slide with an immediate show, etc.
Also try calling .stop(true,true) on the note first: note.stop(true,true).slideDown();. This is in case it is busy with some other animation and the slide down is being queued.
Try this
note.slideDown().delay(timing).slideUp();
i think that the problem might be in the easing function used by the slideDown which is swing by default (which is logaritmic). try using linear and maybe try using a faster slidedown time
note.slideDown(400, 'linear').delay(timing).slideUp(400, 'linear');
You are not passing a value for duration to slideDown. Try:
note.slideDown(1000, function() {
note.delay(timing).slideUp();
});
I'm working on a site that uses setTimeout() to do kind of a 'slideshow' with banners on my site. Everything works fine, I have the delay set to 10 seconds. The problem only occurs when I switch windows/tabs and do something else. When I come back, the function runs a ton of times I (assume) to catch up or something. Problem is, my screen starts flickering over and over to show all banners fading in and fading out.
Any ideas on a solution? I've noticed this in Google Chrome, I also know it happens in Firefox. Not sure about IE.
EDIT
Here is the snippet I'm dealing with. Sadly, it is part of a very large script and is connected to a very complicated HTML file.
I hope you can get an idea of what is going on here at least:
var lval=0;
var nval=1;
setHead = function(data) {
lval=nval;
var index=1;
$.each(data, function(name,value) {
if (Math.floor(Math.random()*index+2)==index+1) {
nval=index;
}
if (index==lval) {
$('.headmaster').find('img').fadeOut('fast');
//$('.headmaster').css('background-color',value.backgroundcolor);
$('.headmaster').find('a').attr('href',value.url);
$('.headmaster').animate({backgroundColor:value.backgroundcolor},'slow',function() {
$('.headmaster').find('img').attr('src',value.img);
$('.headmaster').find('img').fadeIn('slow');
});
}
index++;
});
setTimeout(function() { setHead(data); },10000);
}
arrayGet = function(arr,idx) {
$.each(arr, function(i,v) {
if (i==idx) {
return v
}
});
return null
}
$(document).ready(function() {
$.getJSON('json/intros.json', setHead);
});
I'm using jQuery for the animation and the color plugin for fading the colors.
It is happening probably because you are using an old version of jQuery. Namely the one where the dev team has started using requestAnimationFrame API. Fortunately, they fixed it in 1.6.3. Here is an extract from their blog:
No more animation “worm holes”: We had high hopes for the browser’s requestAnimationFrame API when we added support into version
1.6. However, one of the highest-volume complaints we’ve received since then relates to the way requestAnimationFrame acts when a tab is
not visible. All the animations initiated when the tab is invisible
“stack” and are not executed until the tab is brought back into focus.
Then they all animate at warp speed! We’ve removed support for this
API (which has no impact on the way you call jQuery’s animation
features) and plan to incorporate it into a future version of jQuery.
Simply update to 1.6.4.