Firefox scrollTop problem - javascript

I have a problem with Firefox scrollTop value and onscroll event. This works great in IE, Safari and Chrome but Firefox seems to lag.
I tried to update some background position with the onscroll event, but when I take the handle and drag it up and down quickly, Firefox stops updating the scrollTop value and it causes some lag in my app.
You can try this code and look in the Firefox console when dragging the handle and you will see the values something stops the updating :
function SaveScrollLocation () {
console.log(document.documentElement.scrollTop);
}
window.onscroll=SaveScrollLocation ;
Any idea how to make Firefox respond more quickly?

There are two ways to handle this - throttle (execute the function with a set interval) and debounce (execute the function after the specified time has passed since the last call). You'll probably want to use throttling in your situation.
A simplified solution may look something like this (Updated: see it at http://jsfiddle.net/yVVNU/1/):
window.onscroll=catchScroll;
var timeOutId = 0;
var jitterBuffer = 200;
function catchScroll()
{
if (timeOutId) clearTimeout (timeOutId);
timeOutId = setTimeout(function(){SaveScrollLocation()}, jitterBuffer);
}
function SaveScrollLocation () {
console.log(document.documentElement.scrollTop);
alert('scrolled');
}
You can also use this jQuery plugin: http://benalman.com/projects/jquery-throttle-debounce-plugin/

$(window).scrollTop() worked for me

Wouldn't the behavior of dragging the window up and down quickly be considered abnormal?
In my view, I wouldn't want to be saving the state if the user is doing that. I'd rather wait until the window has been in the same spot for at least 250ms before recording it's position. The minor variances in position while the user is slamming the scrollbar up and down are probably not very important to the user, know what I mean?
With a little setTimeout magic, couldn't you sidestep this issue AND make your script a little lighter on the browser UI by not firing the SaveScrollLocation until it clear the scroll location is WORTH saving?

Firefox does not (or did not used to) fire the onscroll event as frequently as the other browsers. see here
Interestingly the scrollTop does update at the correct frequency so you can probably use another event such as mousemove. What i did was something like this :
on first scroll event, start listening to mouse move events - update whatever it is you want to based on the scrollTop which does update correctly. After a short timeout has elapsed after an onscroll, stop listening for mouse move events.

var last = +new Date;
function SaveScrollLocation () {
var now = +new Date;
if (now - last > 50) {
// ...
last = now;
}
}
window.onscroll = SaveScrollLocation ;

Related

Custom Scrolling in Js & jQuery : One scroll event

I'm currently listening for scrollevents in jQuery, using the following code:
$(window).on('wheel', function(e) {
var delta = e.originalEvent.deltaY;
if (delta > 0) //do something
else //do something else
});
The issue I'm having is that when the user scrolls, lots of wheel events are fired.
I want each time the user scrolls to only process the first scroll event.
I.E.: I only want to process the first wheel event and then stop listening/processing them until the user stops scrolling. At this point I want to start listening again so I can handle the next scroll.
I've seen mousewheel DOMMouseScroll used rather than .on('wheel') but don't know enough about the differences to know if that could help.
Cheers,
James
You can use the jQuery debounce plugin to rate limit your function. The following code will limit the callback to executing once every 250ms.
$(window).scroll($.debounce(250, true, function(){
console.log('Scrolling!');
}));
It's also possible to use the Lodash or Underscore libraries as they also have a debounce function.

Disabling Mouse Wheel for X Milliseconds with javascript?

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.

DOM element visiility and requestAnimationFrame

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.

How do I detect whether scroll events are fired *only once* like on touch devices?

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).

Remove window "resize" listener

This question relates closely to the stack overflow question "window.resize event firing in Internet Explorer".
The Issue:
I am attempting to fix a resizing issue in Internet Explorer 8. Currently, the resize function gets called repeatedly causing IE to essentially lock up - the user can no longer use buttons that call Javascript actions.
Previous Attempt(s):
var resizeTimeout;
var resizeHandler = function() {
clearTimeout(resizeTimeout);
//$(window).unbind('resize', resizeHandler);
//window.removeEventListener('resize');
window.removeEventListener('resize', resizeHandler, false);
scrollHandler();
setTimeout("$(window).resize(resizeHandler);", 100);
return true;
}
//$(window).resize(resizeHandler);
window.addEventListener('resize', resizeHandler, false);
Problems: It appears that window cannot implement addEventListener or removeEventListener and unbinding jQuery doesn't stop IE from continuing to freak out. It works fine in all other browsers.
Desired Behavior: The goal here is really to get IE to stop repetitively executing code so other functions like onclick events work.
Does anyone know how I can remove the resize event after it's been added or simply make IE stop being retarded. (<-- Extra points if you can make IE not be retarded.)
Resolution: Inside of the scrollHandler function a variable was not declared using the var prefix. Adding var made all the evil fairies go away.
I think you're going about this the wrong way. What you should be doing is using that timeout to block the invocation of "scrollHandler()" until the window resizing activity has paused for a little while (like the 100ms delay you're using).
var resizeTimeout;
function resizeHandler() {
cancelTimeout(resizeTimeout);
resizeTimeout = setTimeout(scrollHandler, 100);
}
$(window).resize(resizeHandler);
Trying to do DOM updates (which I assume to be what goes on inside "scrollHandler") in a "resize" handler is really not a good idea in any browser. By doing that, you won't need to get rid of the "resize" handler at all.
edit — OK now I see that that's effectively what you were trying to do. I still think it's a lot simpler this way.

Categories