I've implemented a scroll based state change on my nav bar, which fades in a fixed position nav at the screen top after the user scrolls down a lengthy page and then hides it and puts it back in it's relative position again as they scroll back up to see the top of the page. It works great 90% of the time. Here's my code..
function scrollNavReveal() {
var nav = $('nav.nav-primary');
$(window).scroll(function(){
var scrollpos = $(this).scrollTop();
if ((scrollpos > 200) && (scrollpos < 800)) {
nav.fadeOut(500);
}
else if (scrollpos > 800) {
nav.css({"position": "fixed", "width": "100%"}).fadeIn(500);
}
else {
nav.css({"position": "relative", "width": "100%", "display": "block"});
}
});
}
The issue is that when I scroll to the page top very quickly with my mouse, sometimes the nav does not appear. I have to make a small scroll on the mouse and then it pops back into place. This also happens when I use my scroll to top function which is initiated when a user clicks the 'go to top' button in the footer.
function go_to_top() {
$('a.naada-top').click(function() {
$('html, body').animate({scrollTop:0}, 'slow');
})
}
In the go_to_top function I believe the .animate({scrollTop:0}, 'slow'); does not take into account the height of the nav itself, which isn't currently in position: relative; (hence not in the normal DOM flow/scroll height).
Both issues seem very similar, but slightly different and they can be seen and tested here naada.staging.wpengine.com
Thanks for your help!
I recommend adding an if statement for < 200 rather than an else at the end of the statement. If you don't have to roll through each if statement and prove them false the processing speed will be quicker, which might catch the quicker scroll a little better. I also use the (document).scroll(function(){ instead of window... but it should work either way.
Here's how I would code this:
$(document).scroll(function() {
var scroll_dst = ($(document).scrollTop());
if (scroll_dst < 200){
nav.css({"position": "relative", "width": "100%", "display": "block"});
}else if((scroll_dst >= 200) && (scroll_dst < 800)) {
nav.fadeOut(500);
}else if((scroll_dst >= 800){
nav.css({"position": "fixed", "width": "100%"}).fadeIn(500);
}
});
You could also make it so when you click the 'go to top' function it automatically places the nav.css back to it's relative state. It's not a perfect solution, but it's better than having no nav at all.
Apparently functions attached to scroll events can be very resource heavy and I think the biggest issue with my code is that the browser couldn't evaluate each of my if statements for every pixel that was scrolled, especially when you scrolled fast to the page top. So I've found a more elegant solution.
In this solution the scroll information is stored in a boolean and he uses a setInterval to check on the scroll position. It's not exactly the same interactivity that I had before, but based on the circumstances it seems the most elegant and resource friendly option. It's actually really pretty code..
You can see it live here.
Related
I have a function to let a container jump to the top upon scrolling 100px, and jump back in position once scrolled to the top again. I am working to disable this on smaller devices but the code I use also breaks the behaviour.
$(window).scroll(function() {`
//After scrolling 100px from the top...
if ( $(window).scrollTop() >= 72 || w > 920 ) {
$('#container').css('top', '0px');
//Otherwise remove inline styles and thereby revert to original stying
} else {
$('#container').attr('style', '');
}
});
UPDATE: this code should actually make the container jump to the former position when you scroll to the top again. The problem is in 'w > 920'. I need that to disable the behaviour on smaller devices but it also breaks the function. I guess because of the 'AND' statement. The window width stays the same so possible because of that the revert breaks.
Not working fiddle
https://jsfiddle.net/xm1yq4to/
Working fiddle
https://jsfiddle.net/s15g7nup/
Is there another way to specify the devicewidth for this?
I ended up doing this in a different way bij not checking the device with but a change of class
$(document).ready(function() {
// run test on initial page load
checkSize();
// run test on resize of the window
$(window).resize(checkSize);
});
//Function to the css rule
function checkSize(){
if ($(".desktop-menu").css("display") == "block" ){
$(window).scroll(function() {
//After scrolling 100px from the top...
if ( $(window).scrollTop() >= 100 ) {
$('#container').css('top', '0px');
}
//Otherwise remove inline styles and thereby revert to original stying
else {
$('#container').attr('style', '');
}
});
}
return false;
};
Here's a full codepen of the situation I will describe in detail below: https://codepen.io/Adam0410/full/MGXjaz/
The javascript contained in the codepen (the core of the issue) is below:
var collapsed = false;
$(window).scroll(function(){
var scroll = window.pageYOffset || document.documentElement.scrollTop;
if (scroll > 207 && !collapsed) {
$("#header").css({
position: "fixed",
height: "50px",
"line-height": "50px"
});
$("#content").css("margin-top", "207px");
$(document).scrollTop(scroll - 50);
collapsed = true;
} else if (scroll < 155 && collapsed) {
$("#header").css({
position: "static",
height: "257px",
"line-height": "257px"
});
$("#content").css("margin-top", "0");
$(document).scrollTop(scroll + 50);
collapsed = false;
}
});
I am attempting to make a large header that is part of the flow of the document, that then turns into a smaller fixed header as you scroll down past it. I want the action of the user scrolling to be smooth during this process.
If you view the pen on mobile (or use chrome's device toolbar) with smooth scrolling and scroll slowly around the breakpoint where the header changes you can see it's completely smooth.
However if you view it on a desktop (with chrome again, or any other browser) the scrolling with a scroll wheel is done in 100-pixel increments. For this reason once again if you scroll around the breakpoint where the header changes you can see it is not smooth.
This occurs since the 100-pixel scrolling doesn't occur instantly and in the process of changing the scrollTop of the document the 100-pixel scrolling animation gets canceled. Is there any way to detect and resume this scrolling animation once I've set the scrollTop property?
Please check https://codepen.io/anon/pen/PeabEL
Changed the js to
$(window).scroll(function(){
var scroll = window.pageYOffset || document.documentElement.scrollTop;
var newHeight = 50;
if(257-scroll>50)
newHeight = 257-scroll;
$("#header").css({
position: "fixed",
height: newHeight+"px",
"line-height": newHeight+"px",
});
});
also added
#content {
...
margin-top:257px;
}
and,
#header {
...
position: fixed;
}
I have another approach with the help of a very small plugin called smoothwheel (Get it here). I extended the answer of #Rohith Murali and created an example. Have a look here:
https://codepen.io/anon/pen/NMBdpx
The plugin itself enables you to scroll smoothly with low impact on performance. Does this fit your needs?
I am using bootstrap 3 and have a fullscreen hero unit at the top of my page, below that is my navigation. I have some js which allows my navbar to stick to be fixed at the top after you scroll past the full screen hero. Also some js for my smooth scrolling links.
The problem is the offset is different before you scroll past the full screen hero and after. But it works fine when you are past the jumbotron. I have tried a bunch of different things but I can seem to get this to work exactly.
Check out the fiddle here.
Here is my js for the smooth scrolling links:
$(document).ready(function() {
// navigation click actions
$('.scroll-link').on('click', function(event){
event.preventDefault();
var sectionID = $(this).attr("data-id");
scrollToID('#' + sectionID, 750);
});
// scroll to top action
$('.scroll-top').on('click', function(event) {
event.preventDefault();
$('html, body').animate({scrollTop:0}, 1200);
});
// mobile nav toggle
$('#nav-toggle').on('click', function (event) {
event.preventDefault();
$('#main-nav').toggleClass("open");
});
});
// scroll function
function scrollToID(id, speed){
var offSet = 95;
var targetOffset = $(id).offset().top - offSet;
var mainNav = $('#main-nav');
$('html,body').animate({scrollTop:targetOffset}, speed);
if (mainNav.hasClass("open")) {
mainNav.css("height", "1px").removeClass("in").addClass("collapse");
mainNav.removeClass("open");
}
}
if (typeof console === "undefined") {
console = {
log: function() { }
};
}
By changing var offSet = 95; I am able to adjust the offset but what would be the best way to use 180 before the navbar sticks to the top but 95 when it does?
Also here is the js I am using for my navbar:
$(function () {
/* $(".navbar-fixed-top").css({"top":$(".jumbotron").height()});
$(window).resize(function (e) {
$(".navbar-fixed-top").css({"top":$(".jumbotron").height()});
});*/
$(document).on( 'scroll', function(){
console.log('scroll top : ' + $(window).scrollTop());
if($(window).scrollTop()>=$(".jumbotron").height())
{
$(".navbar").addClass("navbar-fixed-top");
}
if($(window).scrollTop()<$(".jumbotron").height())
{
$(".navbar").removeClass("navbar-fixed-top");
}
});
});
Are you open to angular.js? I have a directive i use for this. As seen here.
I'll grab the plunker link for you. you might find the code helpful.
Essentially you need to create a ghost dom element to take the place of the menu when you pull it to an new layout position.
EDIT: Here it is
I won't suggest grabbing angular just for this. But you can use the basis of the events and logic to build your own solution.
This here is creating an element and placing in its place
$scope.spacer = $element.after(
'<div class="spacer" style="height:' + $element[0].clientHeight + 'px"> </div>').next();
then this element is removed when the menu is back to its static position.
Inspect the dom and watch how it changes, this will probably help you see the events and changes that need to take place.
EDIT 2 SOLUTION:
HERE is the concepts applied to your JSFiddle
It's not the best solution but by adding margin: 0 0 -100px 0; to your .navbaryou lose the spacing issue.
Also you're getting 22 console errors because of missing images. I'm not saying that this is causing any major problems but you would be better off losing them.
The problem is that when you have not scrolled past the hero, navigation is still part of the layout and pushes content bellow it a little lower. When you scroll past (either manually or via a script) the hero, navigation is removed and fix positioned. That makes everything which was bellow to "jump up" exactly of the navigation height.
That means if portfolio was 1000px from the top, on click you say: go 1000px from top; but then porfolio moves 100px up (as explained above) meaning it is now 900px from the top while the window scrolled 1000px as you asked.
When you have scrolled past the hero, nothing changes its position.
How can I make it so that a viewer cannot scroll up (past a specific point)?
Making it so a viewer cannot scroll is easy with:
body{
overflow: hidden;
}
but that disables scrolling down too.
Detailed description
what I want is some javascript/jquery code that will not allow scrolling up past a given parameter, while the viewer can still scroll up and down before that parameter is reached, but after it is reached they can only scroll up as long as it's not scrolling up past the given parameter
I have absolutely no idea how to go about doing this, any ideas?
You could set a physical point and say something like:
$(function() {
var scrollPoint = 200;
var scrolledPast = false;
$(window).scroll(function() {
$(window).scrollTop() > scrollPoint ? scrolledPast = true : '';
$(window).scrollTop() < scrollPoint && scrolledPast == true ? $(window).scrollTop(scrollPoint) : '';
}).scroll();
});
Fiddle
Although the disabling of scrolling seems a bit counter-intuitive-- why not just hide stuff off the page itself?
I'm looking for a way to prevent the elastic scrolling that occurs on OS X in both Chrome and Safari.
If you don't understand what I mean, it's when you scroll up while the page is at the top, or down when the page is at the bottom, and it shows a gray background behind the page.
There is a css solution for single page apps where you just add overflow:hidden to the html or body
However, I need to be able to scroll.
I've come up with a solution using Javascript (JQuery), but it's only for scrolling passed the top, and only works for chrome. Also, it's a bit buggy in Safari.
$(window).on('scroll', function(e){
scrollAmount = $(this).scrollTop();
if(scrollAmount < 1){
$(this).scrollTop(1);
}
});
So that's just checking of the user scrolls below 1 meaning they try to scroll up passed where the page ends. I tried 0 but that didn't work.
I haven't been able to find a way to check if the user scrolls passed the bottom of the page.
So any thoughts?
Edit:
$(window).on('scroll', function(e){
scrollAmount = $(this).scrollTop();
if(scrollAmount < 1){
$(this).scrollTop(1);
}
if(scrollAmount > $(document).height() - $(window).height()){
$(this).scrollTop($(window).height());
}
});
Now I've added a check for if we scroll passed the bottom of the page. This method is not working though, it's bouncing around very ungracefully.
When you quickly scroll up to the top, the elastic browser causes the scroll top to become negative. Using the st <= 0, will make sure no action is taken when this happens.
$(window).on('scroll',function(){
var dh = $(document).height(),
wh = $(window).height(),
st = $(window).scrollTop();
if (st <= 0) {
$(this).scrollTop(0);
} else if (st + wh >= dh) {
$(this).scrollTop(dh);
}
});
You can now use overscroll-behavior:
html, body {
overscroll-behavior: none;
}
While I dislike the methodology, you can use jQuery to get the document height (and thus the bottom of the page for the scroll distance using $(document).height();)
Another method would be to wrap the entire page with a <div id="preventScroll"></div> wrapper, with the property overflow: scroll; height: 100%; width: 100%;
That would be a separate scrolling device and prevent your whole issue.