I need a sticky bar to be visible and fixed after a certain scroll. I was going to use scroll event, then I met Mozilla page which advise to use window.requestAnimationFrame as the following :
var last_known_scroll_position = 0;
var ticking = false;
function doSomething(scroll_pos) {
// do something with the scroll position
}
window.addEventListener('scroll', function(e) {
last_known_scroll_position = window.scrollY;
if (!ticking) {
window.requestAnimationFrame(function() {
doSomething(last_known_scroll_position);
ticking = false;
});
}
ticking = true;
});
I have a few questions :
1 - What is the logic behind ticking ?
2 - It seems to me that ticking will always be true since if statement doesn't return anything. This will lead to doSomething function will be called only once. I guess I am wrong, what am I missing?
mozilla link of scroll
Default browser behavior
On each scroll event (be it 1 pixel or more), the browser will repaint the layout. (for obvious layout purpose)
The performance problem
Your function doSomething() may not finish before two triggered scroll events. You can see it as a stack of doSomething functions because there will be more calls than your execution time can handle.
requestAnimationFrame()' solution
This function is defined in the window object by default. It tells the browser to wait the execution of the passed function before repainting the layout.
The idea of ticking
As you can see, ticking will only be false after doSomething(). It avoids the previous mentioned stack of events. It avoids executing the code of another scroll event before the function finished and the browser repainted the layout.
Conclusion
It adds performance to your scroll. I asked some days ago a question which illustrates this situation.
Related
I have a simple example where a user starts to scroll on a touch screen, and then after one second, I want to disable scrolling. I thought event.preventDefault() would stop the scrolling but it doesn't seem to work after scrolling has already started
Here is an example: https://jsfiddle.net/7s5m8c6L/30/
let allowScroll=true;
function TS(e){//touchstart handler
setTimeout(function(){
allowScroll=false;
},1000)
}
function TM(e){//touchmove handler
if(!allowScroll){
e.preventDefault();
}
}
In this example, you can start scrolling, and after a second, I want the scrolling to stop, but it does not. I know there are ways that I can get this to work with CSS (adding overflow:hidden), but I would particularly like to know why preventDefault doesn't work.
If you are using chrome, there is a hint in the console:
[Intervention] Ignored attempt to cancel a touchmove event with cancelable=false, for example because scrolling is in progress and cannot be interrupted.
The problem is exactly that Event.cancelable. Unfortunately for you this property is read-only and it is not safe to call preventDefault for a not cancelable event. If you print e.cancelable in the TM function you can observe that throughout the scrolling e.cancelable is false.
I have a spin function which makes the number rotates every 4 seconds. Now I need to start that function once when the numbers are on viewport, so when on viewport start function only once, I don't mean to rotate the numbers only once, just the function to start only once, and when numbers are not in the viewport anymore then do nothing and so on.
My problem comes when I play scrolling while numbers are visible, because that makes the function spin, to fire many times.
I kinda understand what is going on here, but just can't manage it in javascript, everytime I scroll, the scrolling listener fire multiple times so it also fires multiple times the .each() and also the spin function? If someone can explain what is the problem and how to solve it, would be nice.
If you have a fiddle to share would be nice.
Fiddle
var slots = $('.numbers').find('.slot');
if( slots.length ){
var isScrolling;
window.addEventListener('scroll', function ( event ) {
// Clear our timeout throughout the scroll
window.clearTimeout( isScrolling );
// Set a timeout to run after scrolling ends
isScrolling = setTimeout(function() {
slots.each(function(){
let slotHeight = $(this).height();
let animateTo = (-slotHeight + 21);
if( isElementInViewport( $(this)[0] ) ){
if( ! $(this).is(":animated") ){
spin($(this), animateTo);
}
else{
return false;
}
}
});
}, 200);
});
}
Thank you.
What you're seeing makes sense, because the scroll event triggers many, many times while scrolling (not just once when the scroll starts or ends or something like that).
If you'd like your behavior to be triggered less often, there are several options available to you. You could keep the scroll event listener and use a debounce or a throttle to reduce the number of triggers. Or you could listen to a different even altogether.
In your case, it sounds like you would like to trigger an action once an element comes into view, and then only once. For that, you could still run your scroll handler, but check if the element in question is visible, and then run the rest of your logic. Once that's done, you could de-register your event handler (scroll handlers are potentially expensive because they run so often).
I want the content of jQuery.fn.resize to only execute after i let go of the mouse button not while i am resizing the window.
I have tried putting onmousedown inside on resize but it didn't work.
$(window).resize(function(){
console.log("resizing")
});
You can't know what happens outside your document, and you're not notified when the resizing ends (a resizing can occur in many ways, not all of them mouse based).
But you could add a timer for the same effect :
(function(){
var timer;
$(window).resize(function(){
if (timer) clearTimeout(timer);
timer = setTimeout(function(){
// do the things
}, 200);
});
})();
This would add a slight delay (200 ms should be ok) but this would avoid most intermediate recomputing.
Note that you don't receive events when your javascript function is running, so you usually don't have to do this unless you really want the user to not see anything during the resizing.
I have a page in which the user clicks one link to start scrolling down the page automatically for ease in reading. There is another link the user clicks to stop the scrolling. The former works perfectly, but the latter makes the page jump back to the top when clicked instead of stopping the scrolling at the that place on the page. Any ideas?
function pageScroll() {
window.scrollBy(0,1); // horizontal and vertical scroll increments
scrolldelay = setTimeout('pageScroll()',50); // scrolls every 100 milliseconds
}
function stopScroll() {
clearTimeout(scrolldelay);
}
I tried to add return false; to the second function from something I read on another post, but it didn't help. I don't fully understand the use of return anyhow. Thanks for any help.
I assume that you're doing something like this:
start
stop
The quickest fix is to return false from the onclick event handlers, like this:
start
stop
The idea is to stop the browser from doing the default action of the event (in this case, going to #, which scrolls to the top of the page). Nowadays, the more modern way is to bind an event handler function, then use e.preventDefault() in it, but return false; still works for old-style event attributes.
I am scrolling an overflowing DIV's content by changing the scrollLeft property in Javascript:
setInterval(function(){
$('#scrollbox').scrollLeft($('#scrollbox').scrollLeft()+1);
}, 50);
However, I want to stop this as soon as the user scrolls the content themselves, using the mouse. I tried to detect this using the scroll event
$('#scrollbox').scroll(function(){...});
however, my automatic scrolling above also triggers that event. How can I distinguish this and only react to user-initiated scrolling? (or: how can I stop the above code from firing a scroll event? That would also do the trick)
You could use the .hover(): function to stop the scrolling when the mouse is over the scrollbox element:
http://jsfiddle.net/bGHAH/1/
setInterval(function(){
if(!mouseover)
{
$('#scrollbox').scrollLeft($('#scrollbox').scrollLeft()+1);
}
}, 50);
var mouseover = false;
$('#scrollbox').hover(function(){
mouseover = true;
},function(){
mouseover = false;
});
Edit
Based on your comments I managed to find a jquery plugin from the following site: special scroll events for jquery.
This plugin contains an event which attempts to determine whether scrolling has stopped based on the period of time that has elapsed between the last scroll step and the time the check was made.
To get this to work I needed to slow your interval to just over the latency used by the plugin which worked out to be 310 milliseconds. Doing this meant I had to increase the scroll step to keep it visibly moving.
Here is the link:
http://jsfiddle.net/EWACn/1/
and here is the code:
var stopAutoScroll = false;
$(document).ready(function(){
setInterval(function(){
if(!stopAutoScroll)
{
$('#status').html('scrolling');
$('#scrollbox').scrollLeft($('#scrollbox').scrollLeft()+10);
}else{
$('#status').html('not scrolling');
}
}, 310);
$('#scrollbox').bind('scrollstart', function(e){
stopAutoScroll = true;
});
$('#scrollbox').bind('scrollstop', function(e){
stopAutoScroll = false;
});
});
Hope this helps.
For FF (Mozilla):
document.addEventListener('DOMMouseScroll', handler, false);
For IE, Opera and Chrome:
document.onmousewheel = handler;
Another option is to have an external flag that you can set prior to the programmatic scrolling, and then reset afterwords. If the scroll event is fired and this flag isn't set you know that the user is responsible and can act accordingly.
Unfortunately while this is browser independent and easy to read it could lead you to believe that some user scrolls are programmatic ones. However I would think the occurrences of this is small and may be worth it depending on the app you are writing.
Try wheel event, for most modern browsers
The wheel event is fired when a wheel button of a pointing device (usually a mouse) is rotated.