I have a web site which displays a portfolio using this scrollbar http://manos.malihu.gr/jquery-custom-content-scroller/ .
I want to have an automatic scroll when the page is
fully loaded. When a user pass over the portfolio it stops the scroll and whe, he leaves it starts moving again.
It currently works well but sometimes it's getting slow or sluggish randomly.
Is there something wrong with my js function?
Here's the page having problems : http://www.lecarreau.net/new/spip.php?rubrique5 (I need the change the hosting provider, I know the site is slow).
This is the function:
(function($){
var timerId = 0;
$(window).load(function(){
$("#carousel").show().mCustomScrollbar( {
mouseWheel:false,
mouseWheelPixels:50,
horizontalScroll:true,
setHeight:370,
scrollButtons:{
enable: true,
scrollSpeed:50
},
advanced:{
updateOnContentResize:true
}
});
timerId = setInterval(function(){myTimer()},50);
$('#carousel').mouseenter(function () {
clearInterval(timerId);
});
$('#carousel').mouseleave(function () {
timerId = setInterval(function(){myTimer()},50);
});
});
})(jQuery);
function myTimer()
{
var width = $(".mCSB_container").width();
var left = $(".mCSB_container").position().left;
if (width-left>0) {
var scrollTo = -left+4;
$("#carousel").mCustomScrollbar("scrollTo", scrollTo);
}
}
Is there something obvious I'm missing?
Timing in the browser is never guaranteed. Since all functionality contents for time on a single event loop, continuous repeated tasks sometimes get a little janky.
One thing you could do that might decrease the jank is to cache the jQuery objects you're creating every 50ms:
var mCSBContainerEl = $(".mCSB_container");
var carouselEl = $("#carousel")
function myTimer()
{
var width = mCSBContainerEl.width();
var left = mCSBContainerEl.position().left;
if (width-left>0) {
var scrollTo = -left+4;
carouselEl.mCustomScrollbar("scrollTo", scrollTo);
}
}
Selecting these to elements only needs to happen once. From there they can just reference a variable instead of having to find the element on the page and wrap it in jQuery again and again.
Caching the variable in this case depends on them being in the DOM when the code runs, which isn't guaranteed here. You may have to move the variable assignment and function declaration into the function passed to load.
Also, as John Smith suggested, consider checking out requestAnimationFrame. Here's a shim I use by Paul Irish: http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
Related
I just want to disable certain buttons and show a loading spinner until images are loaded.
This is a similar question- How to detect when an image has finished rendering in the browser (i.e. painted)?
I am new to javascript and am confused why there is not a simple way to determine this! Something similar to this-
document.addEventListener('readystatechange', event => {
if (event.target.readyState === 'complete') {
alert('complete');
}
});
Unfortunately after much searching it seems I will need to use a callback or a promise. I figured it would be a good time to learn about promises/async/await. I am having difficulty attempting to rewrite my functions using promises.
$('#container').on('click', '#imgSearch', function (e) {
$(this).html(`
<i class="fa fa-spinner fa-spin"></i>Loading`);
//various if statements here to check radio buttons
showImgs(search, sort, phpController, limit);
}
function showImgs(search, sort, phpController, limit) {
imgArray = [];
var a1 = $.post(phpController, {search: search, sort: sort, limit: limit}, {dataType: 'json'});
$.when(a1).done(function (response) {
if (response['error']) {
alert(response.error.img);
} else {
var imgs = response;
if (Array.isArray(imgs)) {
for (let i = 0; i < imgs.length; i++) {
//I use setTimeout here to render one image per second
setTimeout(function () {
offset++;
saveImages(imgs[i]);
//calling alertFinished on last img is my temporary solution to removing spinner after render
if (i === imgs.length - 1) {
alertFinished();
}
}, 1000 * i);
}
} else {
saveImages(imgs);
}
}
});
}
;
I use the saveImages function to empty and push to another array as well as other purposes not shown-
function saveImages(img) {
imgArray = [];
imgArray.push(img);
displayImages(imgArray);
}
;
displayImages renders the image while adding classes etc.-
function displayImages(imgs) {
var counter = 0;
imgs.forEach(img => {
var html = '';
html += `<div class="" id='${img.id}'><img src=${img.image}></div>`;
$(html).hide().appendTo(imgSection).fadeIn(1000);
});
}
;
AlertFinished removes the loading spinner from search button.
function alertFinished() {
$('#imgSearch').text('Search');
}
Will someone please explain how I can use promises/async/await in this context? It's unfortunate there isn't a way to use an event listener similar to readystatechange because the alternative is to refactor every function that renders new elements in the DOM in order to disable buttons, show spinner etc. Showing a spinner/loading msg is such a widespread feature I am surprised I am having difficulty implementing it appropriately.
So first off, as you can see from the question you linked to (and comments therein), there isn't a great way to tell when an image is actually painted. So I'll focus on a way you can call some function after images are loaded.
For starters, if you have a bunch of images in your page right as it first loads, and you just want to wait for them to load before doing something, you could try the super simple route which would be the window load event.
window.addEventListener('load', (event) => {
// do stuff
});
But I get the impression you have a situation where you're adding images dynamically and want to do something after they're loaded, so that won't work. Still, you're overcomplicating things, I think. A quick look at your function shows you're calling saveImages and displayImages inside a loop even though they appear to be things you want to do only once after you're done adding images.
Assuming that at some point in your whole process you find yourself with a bunch of images that have been added to your DOM, at least some of which are still in the middle of loading, what you need to do is check for the last image to be loaded and then remove your loading spinner afterwards.
The trick here is figuring out which image is last to load. It won't necessarily be the last one you added because images added earlier on could be larger.
One approach you can use to skip the whole promise/async confusion all together would be to use a recursive function that every so often checks whether all images are loaded and if not waits a bit and then checks again. If you make the wait before the function calls itself again relatively short, this will likely work just fine for you.
Something along these lines:
const registeredImages = [];
// add your images
for (let i = 0, l = someImages.length; i < l; i += 1) {
doSomething(); // add someImages[i] to DOM where you want it
someImages[i].setAttribute('data-id', `image-${i}`); // or whatever way to keep track of it
someImages[i].addEventListener('load', register);
}
// remove spinner once they're loaded
tryToRemoveSpinner();
function register(event) {
images.push(event.target.getAttribute('data-id');
}
function tryToRemoveSpinner {
if (registeredImages.length === someImages.length) {
removeSpinner(); // or whatever
} else {
setTimeout(() => { tryToRemoveSpinner() }, 100);
}
}
An enhancement you could add here would be to put some kind of counter on tryToRemoveSpinner so if some image fails to load for whatever reason it eventually just bails out or runs removeSpinner() anyway or whatever you want to do in the event of an error, just in case.
Related reading: How to create a JavaScript callback for knowing when an image is loaded
I'm working with a script that needs to delay (or have a setTimeout) before any animation loads or initializes, but can't seem to figure out where to put it.
As for delay, if I'm not mistaken, this is used mainly with jquery...So for example: $('id or class here').delay(2000); ...Correct?
As for the setTimeout, if I'm not mistaken, it would be with javascript correct? If so, wouldn't it look something similar to this: setTimeout(function () {function_name},2000); or a slightly different variation of that?
Regardless of these two approaches and trying to add it where I think it should go (using either variations mentioned above), for some reason it just doesn't work right. The console isn't really helping either to check for errors.
In a nutshell, I'm trying to set a delay of 2s (2000ms) before anything starts or initializes.
JS CODE (Where I believe the issue lies):
$(document).ready(function() {
// Additional code here...
// start
BG.init();
// Additional code here...
}
});
Where you have this:
$(document).ready(function() {
Put this:
$(document).ready(function() {
setTimeout(function() {
And then where you have this:
});
// wrapper for background animation functionality
var BG = {
Put this:
}, 2000);
});
// wrapper for background animation functionality
var BG = {
And then, if you don't want to incur the wrath of everyone in the world, indent the stuff inside that new function we just created by one more level. 'Cause indentation is the stuff of life.
There's a lot of 'useless' code for us to help you. Next time share only on a need-to-know bases :)
I've edited your $document.ready block to include the timeout, have a look-see:
$(document).ready(function() {
function initiationProcess() {
// setup logo image
BG.logo = new Image();
BG.logo.onload = function() {
BG.logo_loaded = true;
BG.showLogo();
}
// /../ more code /../
// wire ticker listener
Ticker.addListener(BG.tick);
// start
BG.init();
// /../ more code /../
}
setTimeout(initiationProcess, 2000);
});
Edit:
I'd also like to note that it's considered bad practise (not to mention that it might result in buggy code) to only partly use semicolons in your script file. There's points and counterpoints to using semicolons, but pick a standard and stick to it!
I am reading a jQuery book and trying to do an example. In the example lightning flashes on the screen, and that part works fine. The problem is that when you switch tabs, and then switch back, the lightning starts flashing in rapid succession. The problem is supposed to be fixed using the window.onblur and window.onfocus events, but it's not working. Can anybody see what I am doing wrong?
There are three hidden lightning pictures with different id's, and three different functions that make each one flash. goLightning() sets the interval for each function to go, and stopLightning() should clear that interval. That part is what seems not to be working for all I know. Here is the code:
$(document).ready(function(){
goLightning();
window.onblur = stopLightning;
window.onfocus = goLightning;
var int1; var int2; var int3;
// this function sets the lightning interval
function goLightning() {
int1 = setInterval(function() { lightning_one(); },4000);
int2 = setInterval(function() { lightning_two(); },5000);
int3 = setInterval(function() { lightning_three(); },7000);
}
// this function clears the lightning when the window isn't focused
function stopLightning() {
window.clearInterval(int1);
window.clearInterval(int2);
window.clearInterval(int3);
}
// these three functions make the lightning flash and seem to be working fine
function lightning_one() {
$("#container #lightning1").fadeIn(250).fadeOut(250);
}
function lightning_two() {
$("#container #lightning2").fadeIn(250).fadeOut(250);
}
function lightning_three() {
$("#container #lightning3").fadeIn(250).fadeOut(250);
}
});
I have no idea why this isn't working. It seems like stopLightning() isn't clearing the interval, or window.onblur isn't working. Anyway, any feedback would be helpful. Thanks!
Add stopLightning() to the beginning to goLightning(), before setting the new intervals.
Re-arrange the following lines:
goLightning();
window.onblur = stopLightning;
window.onfocus = goLightning;
var int1; var int2; var int3;
in to this instead:
var int1, int2, int3, w = window;
w.onblur = stopLightning;
w.onfocus = goLightning;
goLightning();
Next, use the variable w instead of window in the stopLightning() function to ensure you use a reference to the proper window. Also, as #Kolink mentioned, it wouldn't hurt to put a call to stopLightning() at the beginning of your goLightning() function to ensure you don't have multiple instances running in the event the focus event gets fired more than once.
EDIT I moved the call to goLightning() after having set up the events - untested but I think it would be better that way?
I've done a little bit of research and have found that using setTimeout() causes a bad memory leak, as seen on this post. I'm hoping to find a remedy or an alternative.
What I have is a small view appearing on the screen when any of my many buttons is touched. At the same time I set a timeout to fadeout the small view after 3 seconds. When the button is first pressed, I also clear the timeout so that I don't continue setting multiple ones. Though now while analyzing my code I see that I'm setting an interval and clearing a timeout. Not sure if this is part of my issue. It looks like this:
var Modal = Titanium.UI.createView({
width:151,
height:83,
owner: null,
myView: null,
});
var modalTimer;
var addModal = function(){
clearInterval(modalTimer);
theView.add(Modal);
modalTimer = setTimeout( function() {
removeModal();
changeTurn();
},3000);
}
playerButton.addEventListener('click',function(){
addModal();
});
Thanks!!!
I may have solved this issue with the following:
var Modal = Titanium.UI.createView({
width:151,
height:83,
owner: null,
myView: null,
});
var modalTimer;
var addModal = function(){
clearTimeout(modalTimer);
theView.add(Modal);
modalTimer = setTimeout(removeModal(),3000);
}
playerButton.addEventListener('click',function(){
addModal();
});
I put my changeTurn() function in my removeModal() function, and removed the anonymous function from setTimeout. I also corrected the clearTimeout confusion. I still have to see how well this holds up over extended gameplay, but from first impressions this has fixed my issues. I hope this helps anyone with similar issues.
I don't know if this helps but I have noticed that my app crashes if i use:
modalTimer = setTimeout(removeModal(),3000);
but doesn't if i use
modalTimer = setTimeout(removeModal, 3000);
where removeModal is defined as
var removeModal = function()
{...};
I have this bit of javascript written with jQuery 1.2.5. It's contained inside the main function() of a plugin that I wrote. The plugin is a horizontal gallery scroller very similar to jCarousel. It does alot of auto calculating of widths and determines how many to scroll based on that and the size of the images, which is what all the calculations are that are going on.
What my question is, how do I prevent this from firing off before a previous execution is finished. For instance, if I get a little click happy and just frantically mash down on .digi_next. Things don't go so well in the UI when that happens and I'd like to fix it :) I thought the answer might lie in queue, but all my attempts at using it haven't turned out anything worthwhile.
var self = this;
$(".digi_next", this.container).click(function(){
var curLeft = $(".digi_container", self.container).css("left").split("px")[0];
var newLeft = (curLeft*1) - (self.containerPad + self.containerWidth) * self.show_photos;
if (newLeft < ((self.digi_gal_width - (self.containerPad + self.containerWidth) * self.show_photos)) * -1) {
newLeft = ((self.digi_gal_width - (self.containerPad + self.containerWidth) * self.show_photos)) * -1;
}
$(".digi_container", self.container).animate({
left: newLeft + "px"
}, self.rotateSpeed);
});
Just use a global busy flag. When you enter your click handler, check it, and only proceed if it's false. Immediately set it to true, and then set it back to false when the animation ends. JavaScript is single-threaded, so there is no race condition to worry about.
var busy = false;
$("...").onclick(function() {
if (busy) return false;
busy = true;
$("...").animate(..., ..., ..., function() {
busy= false;
});
return false;
});
Take a look at jQuery UI. Specifically the effects-part of the plug in. I use the slide-effect on my personal website (click on the arrows at the sides of the boxes).
I prevent users triggering the effect more than once - before the effect has ended - with the one event-handler and a callback function.
Here's the source-code
As an alternative to the afformentioned global flag, you could assign the value to the DOM element, that way enabling multiple elements on the page to have the same behaviour:
$("...").onclick(function(el) {
var self = el;
if (self.busy) return false;
self.busy = true;
$("...").animate(..., ..., ..., function() {
self.busy= false;
});
return false;
});
Since JavaScript functions calls are asyncronus, you can pass as a in parameter a callback function that's called when the previous call ends (same for errors).
You can pass the function you wrote in this post as the callback for the function that fire before.
Hope this helps.
Regards