jQuery toggle class gets called too often on scroll - javascript

I am trying to add a class to my sticky container once the user scrolls the page past 100px, but it is lagging, and toggling the class uncontrollable.
$(function () {
$nav = $(".topmenu-container");
$(document).scroll(function () {
if ($(this).scrollTop() >= 100) {
$nav.addClass('scrolled');
} else {
$nav.removeClass('scrolled');
}
});
});
This is what I have so far.. I had the same function without an if clause and the toggleClass function instead, but same effect there.
Here you see the container that sticks to the top on scroll, and that I want to add the class to once the user scrolls past 100px from top for example
Edit: I now output the scrollTop value and saw, that it seems to get stuck at the point where the toggle should happen. It keeps jumping between 156px and 87px which makes the toggle happen multiple times a second. Does anyone have an idea?

What you need is rate limiting for your scroll events. In your case, I would suggest throttling, which means that your scroll events are limited to a certain number per second or time unit.
Another option is to use debouncing, but this would cause the event to be triggered only once at the end of the scrolling, which might not be the best solution for your case.
Read more here: Difference Between throttling and debouncing a function
Nice visualization: http://demo.nimius.net/debounce_throttle/

Depending on your use case you may actually get away with the new sticky value for the position property in CSS:
https://developer.mozilla.org/en-US/docs/Web/CSS/position
Additionally, instead of using debouncing and throttling, you could consider using the IntersectionObserver API. The events for InteresectionObserver fire only once (depending on settings) and surely less than scroll which is a hard to optimize event since it fires all of the time. See here:
https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
Now, if you have some elements inside the element that depend on the class, such as... the logo having a scaling animation or such, you will definitely have to use IntersectionObserver. It looks like your threshold is 100 pixels, you can definitely configure Interesction Observer to do that.
You can also take a look at this example from Wes Bos which seems to be in one of his free classes:
https://wesbos.com/javascript/06-serious-practice-exercises/scroll-events-and-intersection-observer
I'm hoping you can try these solutions instead of the antiquated scroll thing.

Related

Scroll DIV by scrolling another and the way around

I have two DIVs next to each other. If I scroll the first, the second should scroll too. This is working with this JS Code:
$('#firstDiv').on('scroll', function () {
$('#secondDiv').scrollTop($(this).scrollTop());
});
If I want to apply this to the second DIV too, so that the first DIV scrolls by scrolling the second, I tried it this way:
$('#firstDiv').on('scroll', function () {
$('#secondDiv').scrollTop($(this).scrollTop());
});
$('#secondDiv').on('scroll', function () {
$('#firstDiv').scrollTop($(this).scrollTop());
});
The problem now is, that both are scrolling super slow. Like so slow, that it nearly is not visible.
I guess they start to interact with each other or block each other.
How to do this properly?
They definitively slow down, particularly while scrolling with mouse wheel.
I think it's either use a timeout variable so there's not so many events fired when you're scrolling, or use some javascript code to keep them sync'd directly, like this small <1kb plugin does:
http://asvd.github.io/syncscroll/

Hide NavBar when pageYOffSet = 0

On my site, I have two Navigation Bars, one scrolls up along with the text but the other hides when I scroll up and shows when I scroll down. I just don't know how to make it invisible once it's back to pageYOffset = 0. Anyone knows how, please?
first, welcome to stackoverflow!
to know when the offset is at a particular position, you need to listen for the scroll and resize events, try something like:
function checkPageOffset() {
if(window.pageYOffset === 0) {
// do your menu manipulation here
}
}
window.addEventListener('scroll', checkPageOffset);
window.addEventListener('resize', checkPageOffset);
this way, the function runs on each scroll and each resize and only runs code if the offset is 0. you may prefer to test window.pageYOffset < someMinimumOffset instead in case the scroll is not exactly to 0.
as i was getting at with my comment above, chances are there's already code somewhere in your system that's testing if the offset is greater than some threshold in order to display the menu in the first place, really you should just have to add on to that function to hide the menu if that condition is not met. if you can post that code, we might be able to help you integrate a solution more cleanly with your existing setup.
edit: incorporate listen for resize as per Tracker1's comment below

float:right causes jquery slide toggle dropdown div (activated via hover) to be jumpy?

I have a div that "drops down" thanks to jquery's slidetoggle and hover functions.
Inside that div I have some info (like the date, and a note counter) And I have three buttons.
Two of them are tumblr buttons ( {LikeButton}, {ReblogButton} )
and the third is an entypo symbol.
I have to float them all to the right in their own seperate class or they overlap (A problem of tumblr's, their like and reblog buttons are not well made)
I've identified that it is the "float:right" css bit that is making my jquery slidetoggle dropdown 'jumpy'.
(Or rather, it slides down, and in a millisecond slides up then down again, creating a jittery effect)
Does anyone know what might be causing this? Does anyone know a better way of lining up 3 spans on the right edge of a div (text-align:right doesn't work, I tried that already)
My test site is here: http://test-theme-one.tumblr.com
If it helps
Are you referring to the fact that when you mouse over it, it expands, then slightly retracts? If so try adding the min-height property set to the size of the icons to the .notes element and it seems to prevent this jittering.
Tip:
I noticed if you mouse out right away it will jitter too because it calls the method at the same time. Set a flag when the animation initiates to prevent the other animation from starting. something like this pseudo code:
//Global Flag
flag = false;
-- start function --
//prevent animation if another animation is in progress.
if(flag) return;
flag = true;
your.animation(function(){
//callback (when animation is done)
flag = false; //rest flag
});
The reason why it jitters is because since the elements inside are set to float, the height of the parent element is not affected, therefor the animation treats it as if it's empty. But setting a min-height or just height compensates for that. I'm sure it's more complicated than that but that's what's going on in a nutshell.

How to improve JS scroll performance?

I'm working on revamping my website, and the new one can be found on http://beta.namanyayg.com/
There are mainly two things related to scroll on the site:
To check on which 'page' the user is on, by calculating the top offset and scroll position, then adding a class to the page.
To smooth scroll on menu click.
I've written code for both, but there is a lot of lag.
The first one almost always results in lagging. The second one, as a result, lags too. I have included a boolean to check if it's smooth scrolling and disabled the normal scroll events then, but there's not much change.
Do you have any advice on how to improve performance so there is no (or at least, less) lag? Thank you in advance! :)
...Or is it not related to JS at all? I've optimized everything else...
EDIT: Unminified JS at http://beta.namanyayg.com/js/main.js
If you are using underscore, it has an awesome _.debounce function that is excellent for this sort of thing.
To check how much the user has scrolled from the top of the page (i.e. on which 'page' he is at the moment) can be achieved with:
$(window).scroll(function () {
var scrollAmount = $(window).scrollTop(); // in pixels
if(scrollAmount > SOME_AMOUNT)
{
// add required css class
}
});
To scroll smoothly, to some id for example, you could use:
$("html, body").animate({ scrollTop: $("#someID").scrollTop() }, 1000);
These are both jQuery solutions, so you should have jquery library included. There is also a nice jQuery plugin called waypoints that performs these calculations. It might prove useful to you and it has some other nice features and examples.
I have the same problem. I have a scrollable div with thousands of smaller divs. Every time I call scrollTop to get the scroll-position or set it, it sometimes waits at least 1 second.
I read these slides: http://www.slideshare.net/nzakas/high-performance-javascript-2011 (especially slides 138-139) and now I realize that every call to scrollTop, even as a getter, makes javascript relayout the page. This is most likely the cause of delay, but unfortunately I have not found a solution yet, as in a way to call scrollTop without causing relayouts.
Note: I've only been testing on Chrome.
Also read 'Browsers are smart' section of this article: http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/
I've found an easy solution to the lag with getting scrollTop, just call it inside a scroll-handler and save the result in a variable.
for example in jQuery:
var scrollPos = 0,
element = $('.class');
element.scroll(function(){
scrollPos = element.scrollTop();
});
For the second problem, setting the scrollTop, I reduced the amount of DOM elements by only showing the visible elements. In your case make sure only the visible page(s) are added to the DOM. when scrolling to the next page, in the scroll handler remove the top one (use jQuery .detach) and append the next one to the DOM.

Trigger an event when user clicks on a divs horizontal scrollbar arrow

I have a div with a horizontal scroll.
Is there any way I can detect the click on the horizontal scrolls
arrow using jQuery ?
Note:
Actually I want the scroll to move a fixed no of pixels to the right when the user clicks the right scroll arrow and vice versa.
The event should not be triggered on scroll. It should be only triggered if user explicitly clicks the scrolls arrow.
There are multiple divs having scrollbars, having same class and no ids.
Would prefer to not use any plugins
Here is a demo for what you want
http://jsbin.com/opufow/4/edit
I hope this will help you?
you can use .scroll function of jquery.
Edit 2: Another suggestion is to do something like this depending on your implementation of scrolling areas (see working jsfiddle):
function CustomScrollArrow(elementToScroll) {
var $el = $(elementToScroll);
return $('<a>Click me to scroll</a>').css(/*...*/).click(function(){
$el.scrollLeft($el.scrollLeft()+10);
});
}
$('.ScrollAreaClass').each(function(){
// You could choose to append to your scrolling
// areas or their wrapper classes or whatever...
$('body').append(new CustomScrollArrow(this));
});​
Afterwards it's just a matter of styling your handmade arrows.
Edit 1: I've seen you updated your question, so here's an updated answer with an alternative solution.
You can try to circumvent the problem by using a customized scrollbars implementation, for example jScrollPane by Kelvin Luck or any other, whatever. If the solution offers click events on arrows - then you're set. Otherwise just do a bit of tinkering...
I maintain, however, my point of view that unless you are looking to perform an action before the browser executes the arrow click, I would recommend adding an event handler to the actual result of that click, i.e. the scroll.
Doing this will help to avoid inconsistencies across various implementations of scrolling in browsers; will keep working if scrolling is performed in another manner (i.e. swipe gesture); will still work if there's some javascript code that replaces the default browser implementation of scrollbars.
jQuery offers the .scroll handler to capture scrolling and .scrollLeft to determine the resulting position of the horizontally scrolled content.
Try a working jsfiddle or see the code below:
// Cache the initial scroll position:
var initialLeftScroll = $('#wrapper').scrollLeft();
// Bind event:
$('#wrapper').scroll(function (ev) {
// Get new horizontal scroll offset:
var currentLeftScroll = $('#wrapper').scrollLeft();
// Determine the difference
// (did the user scroll horizontally or just vertically?):
var leftScrollDifference = currentLeftScroll - initialLeftScroll;
// Now we can check
if (leftScrollDifference) {
/* Do something here */
}
// Reset the cache:
initialLeftScroll = currentLeftScroll;
});

Categories