jQuery scroll function fire once when element becomes visible - javascript

Hi guys I am using the scroll function on this script but it fires each time a user scrolls. I want it to only fire once when the user scrolls down to #ror. I tried using the fired variable to check if it has already been fired but that didn't seem to work. I know some people have answered this before but this is where i got the fired solution from and cant get it to work only once. Anyone think they can help please?
$( window ).scroll(function() {
var fired = 0;
console.log(fired);
if(fired == 0){
$('#ror').html('');
$('#ror').goalProgress({
goalAmount: 100,
currentAmount: 75,
textBefore: 'progress bar',
textAfter: '',
offset: 10,
});
fired=1;
}
});

You need to move the fired variable outside the scroll function.
As you are doing it now you are reinitializing the fired variable and setting it to 0 each time the scroll event gets fired.
var fired = 0;
$(window).scroll(function() {
console.log(fired);
if(fired == 0){
$('#ror').html('');
$('#ror').goalProgress({
goalAmount: 100,
currentAmount: 75,
textBefore: 'progress bar',
textAfter: '',
offset: 10,
});
fired=1;
}
});

To detect when a given #target scrolls into view, you can look at it's top position, and check if that position is already inside the viewport.
$('#target').offset().top - $(window).outerHeight() > $(window).scrollTop();
That left part of the equation is constant (as long as you don't move anything around, or change the size of the viewport). Therefore it may be wise to move that outside your event handler function. You need to keep in mind that the scroll event is rather expensive, since it fires constantly when you are scrolling, and the browser is already quite busy with the rendering of the viewport.
When the work is done, you can remove the event handler.
$(window).off(event);
So your final code would look something like this:
var triggerAtY = $('#target').offset().top - $(window).outerHeight();
$(window).scroll(function(event) {
// #target not yet in view
if (triggerAtY > $(window).scrollTop()) {
return;
}
// run your task
// remove this event handler
$(this).off(event);
});
Have a look at the demo: https://jsfiddle.net/6whnfa02/1/
Docs:
http://api.jquery.com/offset/
http://api.jquery.com/outerHeight/
http://api.jquery.com/scrollTop/
http://api.jquery.com/off/

$(window).scroll(function(event) {
var eT = $('#ror').offset().top,
wH = $(this).height(),
wSt = $(this).scrollTop();
if(wSt > (eT-wH)) {
alert('you have scrolled to the ror!');
//detach scroll event handler, as we dont want it to fire again
$(this).off(event);
}
}
The above code checks if user has scrolled down to an element. If yes, alert something and detach the scroll event handler for window. You can refer jquery documentation to see the meaning of offset, height and scrollTop.
Now, as #Pevera pointer out, it is costly to attach event handler to window scroll, you should be aware of that. If you have some heavy code execution inside scroll callback, it will hamper in scrolling the page. So, if you have to attach handler to window scroll, run the scroll callback code within a timeout callback. It ensures to run the scroll callback code after certain delay which helps to scroll the page better. Rewriting the above code with timeout will look like this:
var timeout = null;
$(window).scroll(function (event) {
if (!timeout) {
// set a timeout to run after 250ms
timeout = setTimeout(function () {
clearTimeout(timeout);
timeout = null;
var eT = $('#ror').offset().top,
wH = $(this).height(),
wSt = $(this).scrollTop();
if (wSt > (eT-wH)){
alert('you have scrolled to the ror!');
//detach scroll event handler, as we dont want it to fire again
$(this).off(event);
}
}, 250);
}
});
Everytime user scrolls the page, a timeout is set to run after(atleast) 250ms. In the timeout callback, we remove this timeout handler and check if user has scrolled to the element. If yes, we alert something and detach the scroll handler for window so that it doesn't run again.
Please refer to this FIDDLE for better understanding.
More info on this stackoverflow post and John Resig's blog post.

Related

How to stop scroll event listener after first trigger?

I want a scroll event listener to fire when the user scrolls the page for the first time, but then to stop firing after a certain amount of time and just allow the user to scroll normally.
Here's how I've currently got it working:
var scrollcount = 0
// Scroll event listener
window.addEventListener("scroll", function() {
setTimeout(function() {
scrollcount ++
if (scrollcount < 40) {
window.scrollTo(0, 0);
}
}, 1200);
});
The scrollcount variable increments along with scrolling, and 40 is about how much it takes for one scroll up on my laptop's trackpad. If the counter is under 40, the page scrolls back up to the top of the page once the user lets go of the scroll wheel, if it's over 40 it doesn't.
I realise that this is a really bad way to go about this, so I'm wondering if anyone has a more reliable way to do it. I tried to have a removeEventListener method turn off the event listener once setTimeout has finished its delay, but I couldn't get it to target the window. I think removeEventListener would work if the scroll event listener was assigned to a container div, and not the window, but when I tried that the scroll event listener wouldn't work in the first place.
I wanted to avoid jQuery or any other library if I could, but at this point I'll use anything that gets it to work reliably.
You need to give a name to your listener to remove it:
var scrollcount = 0
// Scroll event listener
function scrollListener() {
setTimeout(function() {
scrollcount ++
if (scrollcount < 40) {
window.scrollTo(0, 0);
window.removeEventListener("scroll", scrollListener);
}
}, 1200);
});
window.addEventListener("scroll", scrollListener);
This will jump to the top of the page, after 2 sec from when user start scrolling:
/* Create a one-time event */
function onetime(node, type, callback) {
node.addEventListener(type, function(e) {
e.target.removeEventListener(e.type, arguments.callee);
return callback(e);
});
}
onetime(document, 'scroll', function(e) {
setTimeout(function(){ window.scrollTo(0, 0); }, 2000);
});
see it live here
If you are using jQuery, you can use jQuery's one function, and simplify the code to:
$(document).one('scroll', function(e) {
setTimeout(function(){ window.scrollTo(0, 0); }, 2000);
});
If you want to jump to the top of the page after some animations are finished instead of waiting predefined amount of time, then you should call window.scrollTo(0, 0); after you are done animating. If you are animating using jQuery's effect functions, then you can pass callback function as last argument, and it will be called once the animation is complete. For example you can do something like this:
$(document).one('scroll', function(e) {
$('pre').animate(
{fontSize: "106px"},
2000,
function(){ window.scrollTo(0, 0); }
);
});
see it live here

Remove and Add scroll event handler using jQuery .off or .unbind

I am writing my own image Lazy Loading function that when a div is scrolled to its bottom we load some new images, the height of the container div (#ScrollDiv in this case) is increased and when we scroll to the bottom again we make the same call. This is fine although I pass a 'pagination ID' with each request for more images (this is called appName.featureName.PaginationConstant and in a parent scope) and I want to remove or freeze the scroll event so we don't make other requests or increment the pagination ID. For example:
appName.featureName.lazyLoader = function() {
var currentScroll = $(this).scrollTop(),
divHeight = $(this)[0].scrollHeight,
actualHeight = $(this).height() + parseInt($(this).css('padding-bottom'))
// have we hit the bottom of the Scroll Div?
if((divHeight - actualHeight) <= currentScroll ) {
// yes we have, remove the scroll, see I name this function below
$('#ScrollDiv').off('scroll', appName.featureName.lazyLoader);
// Now get more photos, in the function below I shall re-bind the scroll functionality
appName.featureName.getMorePhotos(++appName.featureName.PaginationConstant);
}
};
// this is the lazyload funtion
appName.featureName.lazyLoad = function () {
$('#ScrollDiv').on('scroll', appName.featureName.lazyLoader);
};
Everything works great apart from the unbinding! I am still able to fire the scroll event handler despite the fact I have tried to remove it once my condition is met with $('#ScrollDiv').off('scroll', appName.featureName.lazyLoader);
What am I doing wrong?
Have you ever tried like this?
$('#ScrollDiv').on('scroll','#ScrollDiv', appName.featureName.lazyLoader);
and
$('#ScrollDiv').off('scroll','#ScrollDiv', appName.featureName.lazyLoader);
or you can use the method bind too
$('#ScrollDiv').bind('scroll', appName.featureName.lazyLoader);
and
$('#ScrollDiv').unbind('scroll', appName.featureName.lazyLoader);
jQuery's .off() function doesn't work that way. If you wanna add and remove only your own scroll handler and leave other 3rd party scroll handlers alone, you want to use
$("#scrollDiv").on("scroll.appName", appName.featureName.lazyLoader);
and to remove all of your own handlers:
$("#scrollDiv").off(".appName");
or, to only remove your own scroll handler, but leave a click handler alone:
$("#scrollDiv").off("scroll.appName");
See the documentation at http://api.jquery.com/off/ for more information.

window.scroll events triggering twice

No matter what method I use to detect scrolling on the page the event is triggered twice. Please see the code for the different methods I have tried.
<body onmousewheel="alert('Why are you alerting twice?')">
or
<script src="js/jquery-1.8.3.min.js"></script>
<script>
$(window).scroll(function(){
alert("Why are you alerting twice?");
});
</script>
or
window.onscroll = please_scroll;
function please_scroll() {
alert("Why are you alerting twice?");
}
I have even tried using $.debounce.
In case it is of any use I will explain what I am trying to do:
When the user scrolls the wheel either up or down, the page will animate the scroll to the next full width content div. I have code that is successfully doing this onclick of my menu, but I would also like it to happen as the user scrolls, essentially auto assisting them with scrolling to each part of my page. This is the function I currently have for scrolling:
function scrollTo(id){
// Scroll
$('html,body').animate({scrollTop: $("#"+id).offset().top - 110},'slow',function(){
animation_active = "false";
});
}
many devices can trigger scroll events which appear to happen once more often. simply use a timeout for that:
var timeout;
$(window).scroll(function() {
clearTimeout(timeout);
timeout = setTimeout(function() {
// do your stuff
}, 50);
});
you can play with the value 50, i recommend something between 50 and 150.

Scroll event doesn't fire unless page moves

I'm looking to get an event to fire when one scrolls "up" from $(window).scrollTop == 0.
If you have the following code:
$(window).scroll(function(){
console.log("scrolling")
});
On a page where the document < window height then that event never fires because $(window).scrollTop isn't changing, but this doesn't mean that there's no mouse scroll input. I want an event to fire on mouse scroll regardless if the page is moving or not.
Seems like what you are looking for:
http://jsfiddle.net/n8eVQ/
$(document).on('mousewheel DOMMouseScroll MozMousePixelScroll', function(event, delta) {
console.log('mousewheel');
//you could trigger window scroll handler
$(window).triggerHandler('scroll');
});
Other way is to capture scroll event on modern browsers which support event capturing phase (IE>8). This can be used for any dynamic element. As jQuery doesn't implement capturing phase, you have to use javascript addEventListener() method. Here an example implementing logic to get scrolling direction for a textarea:
document.addEventListener('scroll', function (event) {
var $elm = $(event.target);
if ($elm.is('textarea')) { // or any other filtering condition
// do some stuff
var direction = $elm.scrollTop() > ($elm.data('scrollTop') || 0) ? "down" : "up";
$elm.data('scrollTop', $elm.scrollTop());
console.log('scrolling', direction);
}
}, true);
-DEMO-
document.addEventListener('DOMMouseScroll', callbackFunction, false);
Solution for firefox; for other browsers see #roasted solution

How can I prevent a custom event from being triggered repeatedly – or can I "reset" the jQuery .one() function?

I have some code, that checks for the visibility of an element on the screen. As soon as it comes in sight by scrolling the page a custom event is triggered. This custom event starts an animation. To prevent this animation to be started over and over the function that starts it is only started once. For that I used the jQuery function .one().
The function to check if element is visible:
checkScrollPos = function() {
if (objTopPos - scrollPos <= windowHeight / 2) {
$(document).trigger('objVisible');
}
}
The function to listen for the event:
evtListener = function() {
//startAnimation() should only be started once
$(document).one(
{
'objVisible': function(event) {
startAnimation();
}
}
);
}
Here it all happens:
$(document).ready(function() {
evtListener();
$(window).scroll(function () {
scrollPos = $(document).scrollTop();
checkScrollPos();
}
}
Now this all works fine but I extended the checkScrollPos() function to also check if the element gets out of sight again, to then stop the animation. That works fine to. Problem is, as the events trigger, the event-bound functions only are executed one time. So when you scroll teh element in sight, then out of sight and then in sight again the animation will not be executed again (what is the correct behaviour of course). Now I would like to have it that the events are triggered exactly one time but EVERYTIME the element gets in or out of sight and not just one time at all. So basicly I need some kind of reset for the
$(document).one()
function – so that everytime the element gets out of sight, I can use the .one() function again. Is there any way to do that?
You have to bind the objVisible event every time the element disappears.
Just call evtListener() after the element is out of sight, so that the objVisible event is bound again.
Your code would be something like this:
checkScrollPos = function() {
if (objTopPos - scrollPos <= windowHeight / 2) {
$(document).trigger('objVisible');
}
if (/* your condition to check if element is out of sight */) {
evtListener();
}
}

Categories

Resources