Scroll Function Firing Multiple Times Instead of Once - javascript

I am trying to create a website that automatically scrolls to each section upon a single scroll action. This means that the code has to check if the page is scrolled up or scrolled down. I believe the code below solves my problem but the scroll action is fired more than once while the page is scrolling. You will see that the first alert in the if statement reaches 5 instead of the desired 1. Any help on the matter would be highly appreciated.
[Note] I am using the velocity.js library to scroll to each section within the container.
var page = $("#content-container");
var home = $("#home-layer-bottom");
var musicians = $("#musicians");
var athletes = $("#athletes");
var politics = $("#politics");
var bio = $("#politics");
var pages = [ home,musicians,athletes,politics,bio ];
var pageCur = 0;
var lastScrollTop = 0;
page.scroll(function(){
var st = $(this).scrollTop();
var pageNex = pageCur + 1;
if (st > lastScrollTop){
alert(pageNex);
pages[pageNex].velocity("scroll", { container: $("#content-container") });
} else {
alert(pageCur-1);
pages[pageCur-1].velocity("scroll", { container: $("#content-container") });
}
lastScrollTop = st;
pageCur = pageNex;
});

The scroll event (as well as the resize event) fire multiple times as a user does this. To help this, the best practice is to debounce. However, I've never gotten that to work. Instead, I use a global boolean to check if the function has fired.
var scrolled = false;
page.on('scroll', function(){
if(!scrolled){
scrolled = true;
//do stuff that should take a while...
scrolled = false;
};
});

This worked for me!
var ScrollDebounce = true;
$('.class').on('scroll',
function () {
if (ScrollDebounce) {
ScrollDebounce = false;
//do stuff
setTimeout(function () { ScrollDebounce = true; }, 500);
}
})

Related

Scroll to previous div

based on this thread I added a scroll up to next div, like this:
var f = jQuery('.p');
var nxt = f;
jQuery(".next").click(function() {
if (nxt.next('.scroller').length > 0) {
nxt = nxt.next('.scroller');
} else {
nxt = f;
}
jQuery('html,body').animate({
scrollTop: nxt.offset().top
},
'slow');
});
var f = jQuery('.p');
var prev = f;
jQuery(".previous").click(function() {
if (prev.prev('.scroller').length > 0) {
prev = prev.prev('.scroller');
} else {
prev = f;
}
jQuery('html,body').animate({
scrollTop: prev.offset().top
},
'slow');
});
So this scrolls up and down very nicely.
The problem though, is that when the user scrolls, the script doesn't notice it. That is, the user scrolls from div1 to div4, when the user click on my "next"-button, he or she gets scrolled to div2. How can I solve this?
I checked into this but I cannot combine it with the above. There must be an easier way, right?
Any help much appreciated!
Oh... I think I might have solved it myself like this:
var jQuerycurrentElement = jQuery(".scroller").first();
jQuery(".next").click(function () {
var jQuerynextElement = jQuerycurrentElement.next(".scroller");
// Check if next element actually exists
if(jQuerynextElement.length) {
// If yes, update:
// 1. $currentElement
// 2. Scroll position
jQuerycurrentElement = jQuerynextElement;
jQuery('html, body').stop(true).animate({
scrollTop: jQuerynextElement.offset().top
}, 100);
}
return false;
});
jQuery(".previous").click(function () {
var jQueryprevElement = jQuerycurrentElement.prev(".scroller");
// Check if previous element actually exists
if(jQueryprevElement.length) {
// If yes, update:
// 1. $currentElement
// 2. Scroll position
jQuerycurrentElement = jQueryprevElement;
jQuery('html, body').stop(true).animate({
scrollTop: jQueryprevElement.offset().top
}, 100);
}
return false;
});
The above is based on this.
The only problem here is that when parts of a div is scrolled into view, the next and previous buttons sometimes behave strange. For example, when being between div2 and div3 and div 3 is most visible, the previous click can take the user back to div1, which feels not so logical. Can we adjust this somehow? I suppose I would have to do something with the offset but I am unsure.

Android + jQuery - second on swipeleft wont execute

I have to do some Special things for my Webpage to work on Android the correct way. Some Images are displayed (one visible, the other unvisible) and through swipe it should be possible to Change them. No Problem so far on all OS.
But it also should be possible to zoom. Now Android starts to be Buggy. It stops the zoom-gesture because of the swipe callback. The callback itself doesn't Change the page because the view is zoomed, so there should be no break.
Now I work arround through turning my swipeleft and swiperight off while two fingers touching the Display, and tourning back on if the fingers leave the Display.
On First run I can swipe, then I can zoom with no break, but then I can't swipe anymore. The function to set the callbacks back on again is called, it set's the callbacks, but they won't be executed...
Here's the code:
app.utils.scroll = (function(){
var $viewport = undefined;
var swipeDisabled = false;
var init = function(){
$viewport = $('#viewport');
$viewport.mousewheel(mayChangePage);
// On touchstart with two fingers, remove the swipe listeners.
$viewport.on('touchstart', function (e) {
if (e.originalEvent.touches.length > 1) {
removeSwipe();
swipeDisabled = true;
}
});
// On touchend, re-define the swipe listeners, if they where removed through two-finger-gesture.
$viewport.on('touchend', function (e) {
if (swipeDisabled === true) {
swipeDisabled = false;
initSwipe();
}
});
initSwipe();
}
var mayChangePage = function(e){
// If page is not zoomed, change page (next or prev).
if (app.utils.zoom.isZoomed() === false) {
if (e.deltaY > 0) {
app.utils.pagination.prev(e);
} else {
app.utils.pagination.next(e);
}
}
// Stop scrolling page through mouse wheel.
e.preventDefault();
e.stopPropagation();
};
var next = function (e) {
// If page is not zoomed, switch to next page.
if (app.utils.zoom.isZoomed() === false) {
app.utils.pagination.next(e);
}
};
var prev = function (e) {
// If page is not zoomed, switch to prev page.
if (app.utils.zoom.isZoomed() === false) {
app.utils.pagination.prev(e);
}
};
var initSwipe = function () {
// Listen to swipeleft / swiperight-Event to change page.
$viewport.on('swipeleft.next', next);
$viewport.on('swiperight.prev', prev);
};
var removeSwipe = function () {
// Remove listen to swipeleft / swiperight-Event for changing page to prevent android-bug.
$viewport.off('swipeleft.next');
$viewport.off('swiperight.prev');
};
$(document).ready(init);
}());
Pastebin
Any ideas what I can do to get the Events back on again?
Thanks for all Ideas.
Regards
lippoliv
Fixed it:
jQuery Mobile itself prevents the swipe Event if an handler is registered, to kill an "scroll".
So I overwrote the $.event.special.swipe.scrollSupressionThreshold value and set it to 10000, to prevent jQueryMobile's preventDefault-call:
$.event.special.swipe.scrollSupressionThreshold = 10000;
Now my Code Looks like
app.utils.scroll = (function(){
var $viewport = undefined;
var swipeDisabled = false;
var init = function(){
$viewport = $('#viewport');
$viewport.mousewheel(mayChangePage);
// See #23.
$.event.special.swipe.scrollSupressionThreshold = 10000;
// Listen to swipeleft / swiperight-Event to change page.
$viewport.on('swipeleft.next', next);
$viewport.on('swiperight.prev', prev);
}
var mayChangePage = function(e){
// If page is not zoomed, change page (next or prev).
if (app.utils.zoom.isZoomed() === false) {
if (e.deltaY > 0) {
app.utils.pagination.prev(e);
} else {
app.utils.pagination.next(e);
}
}
// Stop scrolling page through mouse wheel.
e.preventDefault();
e.stopPropagation();
};
var next = function (e) {
// If page is not zoomed, switch to next page.
if (app.utils.zoom.isZoomed() === false) {
app.utils.pagination.next(e);
}
};
var prev = function (e) {
// If page is not zoomed, switch to prev page.
if (app.utils.zoom.isZoomed() === false) {
app.utils.pagination.prev(e);
}
};
$(document).ready(init);
}());
Thanks to Omar- who wrote with me several minutes / hours in the jquery IRC and gave some suggestions regarding overwriting Standard values for jQueryMobile.

Lagging JavaScript - Opportunity for optimization or less expensive functions

I have this code and it works exactly as I want. The menu bar sits on top and recognizes the section it is on or in. You can click the links in the yellow menu to move between the sections.
http://jsfiddle.net/spadez/2atkZ/9/
http://jsfiddle.net/spadez/2atkZ/9/embedded/result/
$(function () {
var $select = $('#select');
var $window = $(window);
var isFixed = false;
var init = $select.length ? $select.offset().top : 0;
$window.scroll(function () {
var currentScrollTop = $window.scrollTop();
if (currentScrollTop > init && isFixed === false) {
isFixed = true;
$select.css({
top: 0,
position: 'fixed'
});
$('body').css('padding-top', $select.height());
} else if (currentScrollTop <= init) {
isFixed = false;
$select.css('position', 'relative');
$('#select span').removeClass('active');
$('body').css('padding-top', 0);
}
//active state in menu
$('.section').each(function(){
var eleDistance = $(this).offset().top;
if (currentScrollTop >= eleDistance-$select.outerHeight()) {
var makeActive = $(this).attr('id');
$('#select span').removeClass('active');
$('#select span.' + makeActive).addClass('active');
}
});
});
$(".nav").click(function (e) {
var divId = $(this).data('sec');
$('body').animate({
scrollTop: $(divId).offset().top - $select.height()
}, 500);
});
});
However, the code itself gets quite laggy as soon as you start putting any content in the boxes. I wondered if there is any opportunity to optimize the code and make it run a bit smoother.
The problem you have is that you're repeatedly changing page layout properties (via the animation) and querying page layout properties (in the scroll handler), thus triggering a large number of forced layouts.
If i understand your code correctly you could get a big improvement by disabling the scroll handler during the click animation and instead triggering the effects with no checks made (set the active class on the clicked element).

Add and Remove Class on Scroll Event

I am trying to add class or remove class on getting element top by using This DEMO . Here is the code as well:
$(document).ready(function () {
var sec1_offset = $("#sec1").offset();
var sec2_offset = $("#sec2").offset();
var sec3_offset = $("#sec3").offset();
var sec4_offset = $("#sec4").offset();
var sec5_offset = $("#sec5").offset();
var sec6_offset = $("#sec6").offset();
var sec7_offset = $("#sec7").offset();
$("section").scroll(function () {
if (sec4_offset.top < 100) {
alert("You Are in Sec 4");
}
});
});
I also change the $("section").scroll(function () { to $(body).scroll(function () { and $(document).scroll(function () { but it didn't work!
Can you please let me know what I am doing wrong? Thanks
You can listen to the scroll event of the window object, scroll event like the resize event is fired so many times, for efficiency you can throttle the handler, ie the handler is executed after a specified timeout.
$(document).ready(function () {
var $sec = $("section"),
handle = null;
var $w = $(window).scroll(function () {
// clear the timeout handle
clearTimeout(handle);
// throttling the event handler
handle = setTimeout(function() {
var top = $w.scrollTop();
// filtering the first matched element
var $f = $sec.filter(function() {
return $(this).offset().top + $(this).height() >= top;
}).first().addClass('active');
$sec.not($f).removeClass('active');
}, 50);
}).scroll();
});
http://jsfiddle.net/UTCER/
edit: If you want to add a class to another element, the most efficient way is using the index method:
// Cache the object outside the `scroll` handler
var $items = $('#menu li');
// within the `setTimeout` context:
var $f = $sec.filter(function() {
return $(this).offset().top + $(this).height() >= top;
}).first();
$items.removeClass('active').eq( $sec.index($f) ).addClass('active');
use $(window).scroll for the scroll event listener
also you want to check sec4_offset.top against window.scrollY
JS
$(document).ready(function () {
var sec1_offset = $("#sec1").offset();
var sec2_offset = $("#sec2").offset();
var sec3_offset = $("#sec3").offset();
var sec4_offset = $("#sec4").offset();
var sec5_offset = $("#sec5").offset();
var sec6_offset = $("#sec6").offset();
var sec7_offset = $("#sec7").offset();
$(window).scroll(function () {
if (window.scrollY >= sec4_offset.top) {
alert("You Are in Sec 4");
}
});
});
JSFiddle Demo
Use $(window).scroll()
Here's what jQuery documentation says about scroll event
The scroll event is sent to an element when the user scrolls to a different place in the element. It applies to window objects, but also to scrollable frames and elements with the overflow CSS property set to scroll (or auto when the element's explicit height or width is less than the height or width of its contents).
I know this answer has already been answered, but I'd like to provide an alternative answer on JSFiddle that might accomplish what you're looking for to a more dynamic extent. I would not ask to be voted as the answer, but simply noted as a reference for an alternative approach to this problem
http://jsfiddle.net/mLfAq/5/
$(document).ready(function () {
var offsets = [];
$('[id^="#sec"]').each(function() {
offsets.push([$(this).attr('id'), $(this).offset().top + $(this).height()]);
});
$(window).scroll(function () {
for(var i = 0; i < offsets.length; i++) {
if(offsets[i][1] > $(window).scrollTop()) {
console.log('You are in ' + offsets[i][0]);
return;
}
}
});
});

How to get page scrolling position (y axis)

How can i get the current scrolling position of my browser?, i want to fire events base on page position.This is what I tried:
var scroll_position=document.viewport.getScrollOffsets();
window.onscroll = function (event) {
if(scroll_position>1000)
{
alert('xxxxxxxxxxx');
}
Assuming that you're always going to be testing with window, you can use window.scrollY:
window.onscroll = function (event)
{
if(this.scrollY > 1000)
{
alert('xxxxxxxxxxx');
}
}
jsFiddle Demo
Try with:
window.onscroll = function (event) {
if (window.scrollY > 1000) {
alert('xxxxxxxxxxx');
}
}
As hsz said, do
window.onscroll = function (event) {
var scroll_position = document.viewport.getScrollOffsets();
if (scroll_position > 1000)
{
alert('xxxxxxxxxxx');
}
}
The problem with your code:
var scroll_position=document.viewport.getScrollOffsets();
scroll_position is only set once, when the page loads - therefore it stays the same (probably 0) and the alert never comes up because scroll_position is less than 1000.
hsz put the statement that sets scroll_position into the window.onscroll function, so it is updated every time the page scrolls.

Categories