I was reading an article on HTML5Rocks that gave an example about scrolling through a webpage and checking an array of DOM elements offsetTop's to see if they should be visible.
The article says the best practice way of doing this would be to update a variable with the windows current offset top every time a scroll event is fired. When the first scroll event is fired, it triggers the requestAnimationFrame process of checking offsetTop's of the DOM elements. This decouples the visibility logic from the scroll event.
While I understand the benefit of certainly decoupling these two processes (since the scroll event could be called hundreds of times a second), I can't see the benefit of running the visibility logic every 16ms after the first scroll event, regardless of whether the user has continued to move or not..
Can someone please explain what part of the process I'm missing here?
I think it's well explained in the article.
What else can we do? Well for one thing we are constantly running
requestAnimationFrame and that’s not necessary if we haven’t just
scrolled since nothing will have changed. To fix that we have the
onScroll initiate the requestAnimationFrame
Now whenever we scroll we will try and call requestAnimationFrame, but
if one is already requested we don’t initiate another. This is an
important optimization, since the browser will stack all the repeated
rAF requests and we would be back to a situation with more calls to
update than we need.
Thanks to this setup we no longer need to call requestAnimationFrame
at the top of update because we know it will only be requested when
one or more scroll events has taken place. We also no longer need the
kick off call at the bottom, either, so let’s update accordingly:
var latestKnownScrollY = 0,
ticking = false;
function onScroll() {
latestKnownScrollY = window.scrollY;
if (!ticking) {
requestAnimationFrame(update);
}
ticking = true;
}
function update() {
ticking = false; // reset the tick so we can capture the next onScroll
var currentScrollY = latestKnownScrollY;
// Do visibilty logic and animation here
}
So, "regardless of whether the user has continued to move or not" is not really true. update is only called during (or a littlebit after) the scroll, and at a browser-choosen frame rate instead of a rate of hundreds of events per seconds.
Related
I created some kind of lazy load for images. It works like this:
Every 500ms it check photoes which one you see on device and if you see it begin to load real image and this function will never run for images that loaded but if image cant load it will try again when you see this image again.
Is there any problem to use that kind of function every 500ms?
It sounds like you're using this in response to the images coming into view? Rather than polling every 500ms all the images on the page to see if they're in view, perhaps it would be better to trigger the check only when the page has scrolled (or whatever causes images to scroll. You could still limit this to be no more frequent than once every 500ms, but it has the benefit of:
Not having a 500ms interval loop running all the time
Not checking the images in view unnecessarily
I'll assume the images come in and out of view as the user scrolls, so what you'd want to do in that instance is as follows:
Bind an event to $(window).scroll
Have it fire an event after a fixed period with setTimeout (500ms in your case).
Cancel that timer if the user scrolls again and start it (to prevent it firing multiple times.
Listen for the event firing and perform your image check there.
Here's an example of how the code might look:
var scrollIntervalTimeout;
$(window).on('scroll', function() {
// Cancel the timeout and start it again
clearTimeout(scrollIntervalTimeout);
scrollIntervalTimeout = setTimeout(function() {
$(window).trigger('scrollDidEnd');
}, 500); // 500ms delay before firing the event
});
// Listen for the scrollDidEnd event
$(window).on('scrollDidEnd', function() {
// Perform your image check here
});
I have a Website with RoyalSlider and Mousewheel support. http://www.linus.de/mark/drei.php
Everything works fine, but when i use my macbook (touchpad) the thing is that i fire several mousewheel events at a time when scrolling. so basically i want the script to pause for the time (or a bit less) it takes for one slide to change...
What i would need is a javascript which freezes the mousewheel for x milliseconds each time it's been triggered (after sending 1 or -1 to the slider)...
A Timer with a call back and a flag could work. When you start to scroll you set the flag and not allow the scroll wheel to function, see This Answer on how to disable the scroll wheel. When the timer fires (1 second or so) you reset the flag to let the person scroll again. See This page for how to set up a timer with a call back
I can't give you a full code example since you didn't give any code to us but here's the solution.
When you scroll the mouse, a scroll animation begins. Create a variable somewhere outside the event handler, let's say
var animationInProgress = false;
and set it to true right before the animation begining. Then, this RoyalSlider plugin must have some kind of complete handler (I bet it has - it's paid though) - a parameter where you can put a function to be called when the animation is over. So, you put there a function similar to that:
function() {
animationInProgress = false;
}
The last thing is to check the value of the animationInProgress variable each time you want to run an animation
if (false === animationInProgress) {
//run the animation
}
I hope you get the idea.
I am in the process of profiling Sencha Touch applications and came across the fact that a click of a button triggers action faster as opposed to a click of a list item from Ext.List for the same action. My timeline profiling data indicates that the action is performed on Timer Fired condition for list item. This timer's timeout is 300ms. Now, for buttons there's no timer, so the action is performed as soon as there's touch end (and other Sencha processing common to all clicks).
In my case, the action is a simple transition to another view without any animation.
Following are the screenshot of my timeline data running application on an iPhone 4.
Transition by button click/tap:
Transition by list item click/tap:
I tried to dig into the source code, but could not understand why this is actually happening. My hypothesis is that list waits for that 300ms to see if it was actually a tap action or a scroll action. But is this true? If not, can anyone point me towards the right direction to verify if this hypothesis is true or not?
Any help would be highly appreciated!
I think is because of the pressedDelay config, which for Ext.Button default is 0 and for Ext.DataView is 100 ms.
Aditionally, the button fires directly the handler function. And the dataView executes store.getAt(index) to find the record object and pass it to the itemTap callback what adds some ms.
Got it!
This 300ms wasn't because of the scroll event, rather it was to recognize if its a single or a double tap event. If you look at DoubleTap recognizer source code, it has a maxDuration of 300 ms in the config object. This is used to set the time out for firing singleclick event.
onEnd function:
else {
this.singleTapTimer = setTimeout(function() {
me.fireSingleTap(e, touch);
}, maxDuration);
}
And on every touchStart event, this timeout is cleared.
onTouchStart: function(e) {
if (this.callParent(arguments) === false) {
return false;
}
this.startTime = e.time;
clearTimeout(this.singleTapTimer);
},
To note, this is a private class, so we cannot rely on it. But if anyone wants to decrease the duration between a tap event and firing off its logic, reduce this time. I have noticed that setting it to 150ms will make list item click much much faster, but at the same time, it also opens up the room for ghost click in other screens, since the events are queued up.
You can disable or edit recognizers configuration in Ext.application: http://www.sencha.com/forum/showthread.php?205692-Reduce-delay-of-itemsingletap-on-xtype-list
I've got a table with dozens of rows (up to 100), and each row has 1 element with a mouseover event attached to it. On mouseover, I open a tooltip and need to make an AJAX request to fill it with some data.
My problem is fairly simple: if the user moves his mouse up and down over all the elements that have that event attached, I'm firing tons of requests at a time. I wanna throttle it one way or another, but I'm unsure how to.
I'm gonna have to check whether that same event has been executed in the last n seconds, but how do I keep a link between the firing of event 1 and firing of event 2?
You need a flag in the start of your AJAX call, if the flag is ZERO, return from the function. How you scope the varialbe is up to you.
if(AJAXOK==0) {
return;
}
AJAXOK = 1;
Set the variable in the AJAX call, so that is resets itself after a timeout:
window.setInterval(function() { AJAXOK = 1 },5000);
AJAXOK = 0;
If you're interested in trying a different approach, reactive extensions are designed for exactly this sort of thing:
http://reactive-extensions.github.com/RxJS
iOS devices (and likely Android ones) have a different scrolling behavior: The scroll event is only fired once after the entire scroll is done.
How do I detect whether the browser behaves this way?
I could use window.Touch or Modernizr.touch but they don't tell me anything about the scroll behavior, it would be like asking if someone is French to understand whether they like croissants, right? :)
I think you're right about the detection because there will be some devices that will support both touch and mouse behaviors (like Windows 8 tablets), some will only support touch (phones) and some will only support mouse (desktops). Because of that, I don't think you can conclusively say that a device only has one behavior as some could have both.
Assuming that what you're really trying to do is to figure out whether you should respond immediately to every scroll event or whether you should use a short delay to see where the scroll destination ends up, then you could code a hybrid effect that could work well in either case.
var lastScroll = new Date();
var scrollTimer;
window.onscroll = function(e) {
function doScroll(e) {
// your scroll logic here
}
// clear any pending timer
if (scrollTimer) {
clearTimeout(scrollTimer);
scrollTimer = null;
}
var now = new Date();
// see if we are getting repeated scroll events
if (now - lastScroll < 500){
scrollTimer = setTimeout(function() {
scrollTimer = null;
doScroll(e);
}, 1000);
} else {
// last scroll event was awhile ago, so process the first one we get
doScroll(e);
}
lastScroll = now;
};
doScroll() would be your scroll processing logic.
This gets you a hybrid approach. It always fires on the first scroll event that arrives when there hasn't recently been a scroll event. If there are a series of scroll events, then it fires on the first one and then waits until they stop for a second.
There are two numbers that you may want to tweak. The first determines how close scroll events must be to consider them rapid fire from the same user action (current set to 500ms). The second determines how long you wait until you process the current scroll position and assume that the user stopped moving the scrollbar (currently set to 1s).