Latest Update: Could find the exact JS file dealing with the sticky header.Attached the same.
I have recently started maintaining a website that already built using Drupal.
When a user visits the homepage (https://scholars.umd.edu/) and scrolls down slowly(the issue magically vanishes when we quickly scroll down), the red band at the top seems to get stuck for a while and it is only after scrolling more and more that a user is able to scroll down finally.Please go to the website and scroll down on the home page and you will understand the issue.
I am attaching the relevant Javascript file where I think the issue might be present but I am newbie in JS so not really sure whats going on with the jQuery calls etc.
Update: The issue only happens in Chrome and not in Firefox or IE. the chrome version I am using is Version 59.0.3071.115 (Official Build) (64-bit). Also, the issue happens when I use the mouse to scroll down or when i use the touchpad and scroll down slowly. If I click and drag the scroller on the right, the issue is not present.
jQuery(document).ready(function ($) {
function stickymenu() {
var $this = this,
$body = $("body"),
header = $("#header"),
headerContainer = header.parent(),
menuAfterHeader = (typeof header.data('after-header') !== 'undefined'),
headerHeight = header.height(),
flatParentItems = $("#header.flat-menu ul.nav-main > li > a"),
logoWrapper = header.find(".logo"),
logo = header.find(".logo img"),
logoWidth = logo.attr("width"),
logoHeight = logo.attr("height"),
logoPaddingTop = parseInt(logo.attr("data-sticky-padding") ? logo.attr("data-sticky-padding") : "28"),
logoSmallWidth = parseInt(logo.attr("data-sticky-width") ? logo.attr("data-sticky-width") : "82"),
logoSmallHeight = parseInt(logo.attr("data-sticky-height") ? logo.attr("data-sticky-height") : "40");
if(menuAfterHeader) {
headerContainer.css("min-height", header.height());
}
$(window).afterResize(function() {
headerContainer.css("min-height", header.height());
});
$this.checkStickyMenu = function() {
if(!menuAfterHeader) {
if($(window).scrollTop() > ((headerHeight - 15) - logoSmallHeight)) {
$this.stickyMenuActivate();
} else {
$this.stickyMenuDeactivate();
}
} else {
if($(window).scrollTop() > header.parent().offset().top) {
header.addClass("fixed");
} else {
header.removeClass("fixed");
}
}
}
$this.stickyMenuActivate = function() {
if($body.hasClass("sticky-menu-active"))
return false;
logo.stop(true, true);
$body.addClass("sticky-menu-active").css("padding-top", headerHeight);
flatParentItems.addClass("sticky-menu-active");
logoWrapper.addClass("logo-sticky-active");
logo.animate({
width: logoSmallWidth,
height: logoSmallHeight,
top: logoPaddingTop + "px"
}, 200, function() {});
}
$this.stickyMenuDeactivate = function() {
if($body.hasClass("sticky-menu-active")) {
$body.removeClass("sticky-menu-active").css("padding-top", 0);
flatParentItems.removeClass("sticky-menu-active");
logoWrapper.removeClass("logo-sticky-active");
logo.animate({
width: logoWidth,
height: logoHeight,
top: "0px"
}, 200);
}
}
$(window).on("scroll", function() {
$this.checkStickyMenu();
});
$this.checkStickyMenu();
}
stickymenu();
});
You could be seeing this due to the way Chrome is applying your wheel events. Ben Nadel explain this in more detail.
The solution to your problem most likely boils down to setting overscroll-behavior: contain on your overflow: auto container.
Related
I've got a back to top button that shows up on my webpage that I'm working on. When you scroll down and sometimes when it's clicked it jumps to the top and then jumps back to where you were on the page and then smoothly scrolls to the top like it's supposed to. Keep in mind that it does not do this all the time. Would this just be a lag or glitch issue or if there some error in my script?
$(function(){
$(document).on( 'scroll', function(){
if ($(window).scrollTop() > 615) {
$('.ion-android-arrow-dropup-circle').addClass('show');
} else {
$('.ion-android-arrow-dropup-circle').removeClass('show');
}
});
$('.ion-android-arrow-dropup-circle').on('click', scrollToTop);
});
function scrollToTop() {
verticalOffset = typeof(verticalOffset) != 'undefined' ? verticalOffset : 0;
element = $('body');
offset = element.offset();
offsetTop = offset.top;
$('html, body').animate({scrollTop: offsetTop}, 500, 'linear');
};
Searched 40+ questions and couldn't find an answer. Only saying this because if you don't and somebody finds one they always say, "You should have looked before asking." I see it all the time.
ANSWER TO MY OWN QUESTION
After going so long without responses I had moved on and decided not to worry about this issue at that time. Today, I was working on a different site using the same jQuery script and was having the same problem. I decided to try and fix it myself since I couldn't find help on the issue.
The solution was simple! I don't know how I missed it the first time around. All I did is take the above code and add one function to it:
$('.ion-android-arrow-dropup-circle').click(function(event) {
event.preventDefault()
});
I forgot all about needing to remove the default action of clicking a link which is jumping to the destination. It now works perfectly smooth and looks great, just like I wanted to begin with!
My fully updated script for your reference:
$(function(){
$(document).on( 'scroll', function(){
if ($(window).scrollTop() > 50) {
$('.ion-eject').addClass('show');
} else {
$('.ion-eject').removeClass('show');
}
});
$('.ion-eject').click(function(event) {
event.preventDefault()
});
$('.ion-eject').on('click', scrollToTop);
});
function scrollToTop() {
verticalOffset = typeof(verticalOffset) != 'undefined' ? verticalOffset : 0;
element = $('body');
offset = element.offset();
offsetTop = offset.top;
$('html, body').animate({scrollTop: offsetTop}, 200, 'linear');
};
On the Chrome browser, I get everything working, but, if I want to make it work at the native Android Browser, I can't see the div I want to show. Instead, it shows me the first span with the class leftscroller.
How could this be? I tried Android 4.1.2 and 4.1.3 browsers.
This is the JS code:
$('.navigation-block').on('click', function(){
$(this).parents('#navigation.small').toggleClass('open');
});
/* Scrollable */
var showScrollIndicators = function() {
var subnav = $('#navigation.small .subnav')
if (subnav.length > 0) {
var scrollLeft = subnav.scrollLeft();
var viewportWidth = subnav.innerWidth();
var scrollWidth = subnav[0].scrollWidth;
var leftScroller = $('#navigation.small').find('.leftscroller'),
rightScroller = $('#navigation.small').find('.rightscroller');
leftScroller.addClass('enabled');
rightScroller.addClass('enabled');
if (scrollLeft === 0) {
// we've reached the far left part of the scroll area
leftScroller.removeClass('enabled');
}
if (scrollWidth - scrollLeft <= viewportWidth) {
// we've reached the far right part
rightScroller.removeClass('enabled');
}
}
};
/* Add scroll indicators */
$('#navigation.small .ph_subnav').append(
'<span class="leftscroller">You can scroll to the left</span>' +
'<span class="rightscroller">You can scroll to the right</span>'
);
showScrollIndicators();
$('#navigation.small .subnav').scroll(function() {
showScrollIndicators();
});
What am I doing wrong? So the whole area which falls beneath /Scrollable/ is not shown.
I would like to make it so when user scrolls down and reaches a certain div, say #float, set that div to margin-top: 50px and position fixed, and if user scrolls back up undo those changes. It's hard to understand I know ))) If you go to this page and pay your attention to sidebar once scrolling up and down you will see what I mean.
As you scroll down 2nd advertisement scrolls with a page too.
How would I achieve same functionality with jQuery/CSS?
This is a way of doing it in jQuery.
This code is provided for example purposes only; there are almost certainly a handful of regularly-maintained jQuery plugins that will do this thing for you - check GitHub or DailyJS.
$(window).scroll(function() {
var styledDiv = $('#styledDiv'),
targetScroll = $('#float').position().top,
currentScroll = $('html').scrollTop() || $('body').scrollTop();
styledDiv.toggleClass('fixedPos', currentScroll >= targetScroll);
});
Here is a simple JSFiddle of the above in action.
Edit: Have now refactored this code to a more elegant solution.
Edit 2: Following an email I received about a question, I've updated the code above so that it also works in Firefox. As $('body').scrollTop() will not work in Firefox (See comments on the jQuery API page), we need to check both the html and body elements.
This is the relevant jQuery/JavaScript code use on that site.
if (window.XMLHttpRequest) {
var topGagStay = $("top-gag-stay");
var isLoggedIn = $("profile-menu") ? true : false;
var sidebarAdsTop = 1061 - 545;
var signupBtnOffset = 60;
var dockPos = 72;
if (!isLoggedIn && !GAG.isReadOnly()) {
sidebarAdsTop += signupBtnOffset
}
if (formMessageShown) {
sidebarAdsTop += formMessageOffset
}
if (topGagStay) {
if (document.documentElement.scrollTop > sidebarAdsTop || self.pageYOffset > sidebarAdsTop) {
if (topGagStay.style.position != "fixed") {
topGagStay.style.position = "fixed";
topGagStay.style.top = dockPos + "px"
}
} else {
if (document.documentElement.scrollTop < sidebarAdsTop || self.pageYOffset < sidebarAdsTop) {
topGagStay.style.position = "";
topGagStay.style.top = ""
}
}
}
}
Thank FireBug and http://jsbeautifier.org/ for the code (and 9GAG, of course).
I have tried the above answer by beardtwizzle and it worked fine. Also made it work for the case when the page is scrolled upto the bottom of the page.
see the working demo/tutorial here
Just wondering if anyone has an idea as to how I might re-create a nav bar style that I saw a while ago, I just found the site I saw it on, but am not sure how they might have gotten there. Basically want it to scroll with the page then lock to the top...
http://lesscss.org/
Just do a quick "view source" on http://lesscss.org/ and you'll see this:
window.onscroll = function () {
if (!docked && (menu.offsetTop - scrollTop() < 0)) {
menu.style.top = 0;
menu.style.position = 'fixed';
menu.className = 'docked';
docked = true;
} else if (docked && scrollTop() <= init) {
menu.style.position = 'absolute';
menu.style.top = init + 'px';
menu.className = menu.className.replace('docked', '');
docked = false;
}
};
They're binding to the onscroll event for the window, this event is triggered when the window scrolls. The docked flag is set to true when the menu is "locked" to the top of the page, the menu is set to position:fixed at the same time that that flag is set to true. The rest is just some simple "are we about to scroll the menu off the page" and "are we about back where we started" position checking logic.
You have to be careful with onscroll events though, they can fire a lot in rapid succession so your handler needs to be pretty quick and should precompute as much as possible.
In jQuery, it would look pretty much the same:
$(window).scroll(function() {
// Pretty much the same as what's on lesscss.org
});
You see this sort of thing quite often with the "floating almost fixed position vertical toolbar" things such as those on cracked.com.
mu is too short answer is working, I'm just posting this to give you the jquery script!
var docked = false;
var menu = $('#menu');
var init = menu.offset().top;
$(window).scroll(function()
{
if (!docked && (menu.offset().top - $("body").scrollTop() < 0))
{
menu.css({
position : "fixed",
top: 0,
});
docked = true;
}
else if(docked && $("body").scrollTop() <= init)
{
menu.css({
position : "absolute",
top: init + 'px',
});
docked = false;
}
});
Mu's answer got me far. I tried my luck with replicationg lesscss.org's approach but ran into issues on browser resizing and zooming. Took me a while to find out how to react to that properly and how to reset the initial position (init) without jQuery or any other library.
Find a preview on JSFiddle: http://jsfiddle.net/ctietze/zeasg/
So here's the plain JavaScript code in detail, just in case JSFiddle refuses to work.
Reusable scroll-then-snap menu class
Here's a reusable version. I put the scrolling checks into a class because the helper methods involved cluttered my main namespace:
var windowScrollTop = function () {
return window.pageYOffset;
};
var Menu = (function (scrollOffset) {
var Menu = function () {
this.element = document.getElementById('nav');
this.docked = false;
this.initialOffsetTop = 0;
this.resetInitialOffsetTop();
}
Menu.prototype = {
offsetTop: function () {
return this.element.offsetTop;
},
resetInitialOffsetTop: function () {
this.initialOffsetTop = this.offsetTop();
},
dock: function () {
this.element.className = 'docked';
this.docked = true;
},
undock: function () {
this.element.className = this.element.className.replace('docked', '');
this.docked = false;
},
toggleDock: function () {
if (this.docked === false && (this.offsetTop() - scrollOffset() < 0)) {
this.dock();
} else if (this.docked === true && (scrollOffset() <= this.initialOffsetTop)) {
this.undock();
}
}
};
return Menu;
})(windowScrollTop);
var menu = new Menu();
window.onscroll = function () {
menu.toggleDock();
};
Handle zoom/page resize events
var updateMenuTop = function () {
// Shortly dock to reset the initial Y-offset
menu.undock();
menu.resetInitialOffsetTop();
// If appropriate, undock again based on the new value
menu.toggleDock();
};
var zoomListeners = [updateMenuTop];
(function(){
var w = window,
d = document,
e = d.documentElement,
g = d.getElementsByTagName('body')[0];
var lastWidth = 0;
function pollZoomFireEvent() {
var widthNow = w.innerWidth || e.clientWidth || g.clientWidth;
if (lastWidth == widthNow) {
return;
}
lastWidth = widthNow;
// Length changed, user must have zoomed, invoke listeners.
for (i = zoomListeners.length - 1; i >= 0; --i) {
zoomListeners[i]();
}
}
setInterval(pollZoomFireEvent, 100);
})();
Sounds like an application of Jquery ScrollTop and some manipulation of CSS properties of the navbar element. So for example, under certain scroll conditions the navbar element is changed from absolute positioning with calculated co-ordinates to fixed positioning.
http://api.jquery.com/scrollTop/
The effect you describe would usually start with some type of animation, like in TheDeveloper's answer. Default animations typically slide an element around by changing its position over time or fade an element in/out by changing its opacity, etc.
Getting the "bouce back" or "snap to" effect usually involves easing. All major frameworks have some form of easing available. It's all about personal preference; you can't really go wrong with any of them.
jQuery has easing plugins that you could use with the .animate() function, or you can use jQueryUI.
MooTools has easing built in to the FX class of the core library.
Yahoo's YUI also has easing built in.
If you can remember what site it was, you could always visit it again and take a look at their source to see what framework and effect was used.
I want a circle div to lock in the header when the user scrolls past in.
I'm using the following code but it doesn't work
var circle$ = $('.circle'),
oCircleBottom = circle$.offset().top + circle$.outerHeight(true),
window$ = $(window);
window$.scroll(function() {
if (window$.scrollTop() > oCircleBottom) {
}
}.bind(this));
I want to perform an action when the user scrolls pass the circle div; however, the code above does not seem to work. Is oCircleBottom computed correctly?
Enclose your code in $(document).ready function
$(document).ready(function () {
var circle$ = $('.circle'),
oCircleBottom = circle$.offset().top + circle$.outerHeight(true),
window$ = $(window);
window$.scroll(function () {
if (window$.scrollTop() > oCircleBottom) {
$('.circle').css({
position: 'fixed',
top: '0',
left: '0'
});
}
else{
$('.circle').css({
position: 'static'});
}
}.bind(this));
});
You need to take window height into account because if the height of the page isnt enough to scroll down, your code doesnt work. Take a look at this example
However, if the increase page height, you code will work fine without subtracting window height. Take a look at this example
Hence, its better to subtract the window height. jsFiddle
$(window).bind('scroll', function() {
if($(window).scrollTop() >= $('.circle').offset().top + $('.circle').innerHeight() - window.innerHeight) {
//Do you stuff
}
});