I added a sticky header and a smooth scrolling effect, and I cannot figure out how to fix the position so it counts with the header size. The things I have tried disable the sticky header completely.
I have tried to use several different techniques, although I am a newbie and it might be too hard for me to do by myself.
<div id="container">
<section id="sectionHome">
<!--Header and Logo-->
<header id="myHeader">
<logo>
<img src="Pictures/Marvel-logo-880x660.crop.png">
</logo>
</header>
<!--The Top Navigation Menu-->
<div id="mainNav">
<ul>
<li class="current">Home</li>
<li>Characters</li>
<li>Movies</li>
<li>More Info</li>
</ul>
</div>
</section>
//Smooth Scrolling in Main Nav
$(document).ready(function() {
$('#mainNav li a').click(function(e) {
var targetHref = $(this).attr('href');
$('html, body').animate({
scrollTop: $(targetHref).offset().top
}, 1000);
e.preventDefault();
});
});
// Sticky Header
window.onscroll = function() {
myFunction()
}; // When the user scrolls the page
var header = document.getElementById("sectionHome"); // Get the header and top nav
var sticky = header.offsetTop; // Get the offset position of the navbar
function myFunction() { // Add the sticky class to the header when you reach its scroll position. Remove "sticky" when you leave the scroll position
if (window.pageYOffset > sticky) {
header.classList.add("sticky");
} else {
header.classList.remove("sticky");
}
}
This was one thing I tried, but it disabled my sticky header:
$(document).ready(function() {
var headerHeight = $('header').outerHeight(); // Target your header navigation here
$('#main-nav li a').click(function(e) {
var targetHref = $(this).attr('href');
$('html, body').animate({
scrollTop: $(targetHref).offset().top - headerHeight // Add it to the calculation here
}, 1000);
e.preventDefault();
});
});
I thought I could set a value for the total header size and position it that way, although it disables the sticky header. How do I do this properly?
This is my webpage:
http://www.student.city.ac.uk/~aczc972
Best regards,
Danielle
I have added a sandbox how to do it using jQuery, generally speaking only one addition from my site is that I am checking what is the target e.g. scroll to top page, and if yes, I am running specified code for it:
if (targetHref === "#") {
$("html, body").animate(
{ scrollTop: 0 },
"1000"
);
} else {
$("html, body").animate({scrollTop: $(targetHref).offset().top},1000);
}
codesandbox.io/s/81l87w26w0
Subtract header height scroll to prevent covering content by header
scrollTop: $(targetHref).offset().top - 180"
You can also scroll to top of the page like:
Add id="home" to body and change href in:
<li class="current">Home</li>
to home i.e.
<li class="current">Home</li>
Should work with your code
This is not necessarily the best way to do this, but it's an example which is designed to illustrate how it can be done. You don't need jQuery to achieve this effect so it's worth trying it without.
The code below fixes the header, and adjusts the padding of the main wrapper to account for the size of the header. It then sets up listeners on elements with the class section-link. For those elements, the click event will scroll to the element with the id which corresponds to the data-section attribute for the element which was clicked.
You can ignore the css for this which was only added to illustrate how this might work.
const padForHeader = () => {
// find out how high the header element is
const headerHeight = document.getElementById('top-header').clientHeight;
// how much extra padding would we like?
const headerPadding = 20;
// add the two together to see how much padding we need to add
const headerBufferSize = headerHeight + headerPadding;
// set the marginTop property so that the header doesn't overlay content
document.querySelector('.wrapper').style.marginTop = `${headerBufferSize}px`;
};
padForHeader();
// when the window resizes, re-pad for the header
window.addEventListener('resize', padForHeader);
document
.querySelectorAll('.section-link')
.forEach(element => {
// we want to scroll 'smoothly' to the element
const scrollOptions = {
behavior: "smooth"
};
// we can read the data attribute to find the matching element's id
const elementIdToScrollTo = element.dataset.section;
// we can use the id we found to get the corresponding element
const elementToScrollTo = document.getElementById(elementIdToScrollTo);
// we can set the onclick property to scroll to the element we found
element.onclick = () => elementToScrollTo.scrollIntoView(scrollOptions);
});
.header {
background-color: red;
border: solid 2px grey;
border-radius: 5px;
font-family: arial;
margin: 0 auto;
position: fixed;
top: 0;
left: 0;
right: 0;
width: 97%;
}
.header>ul {
list-style: none;
color: rgba(250, 250, 240, 0.8);
}
.header>ul>li {
cursor: pointer;
display: inline-block;
position: relative;
top: 0px;
transition: all 0.3s ease;
width: 200px;
}
.header>ul>li:hover {
color: rgba(250, 250, 240, 1);
top: -1px;
}
.section {
background-color: rgba(20, 20, 30, 0.2);
height: 80vh;
border-bottom: solid 2px black;
padding-top: 50px;
}
<div class="wrapper">
<div id="top-header" class="header sticky">
<ul>
<li class="section-link" data-section="1">Item 1</li>
<li class="section-link" data-section="2">Item 2</li>
<li class="section-link" data-section="hello-world">Item hello world</li>
</ul>
</div>
<div id="1" class="section">
Test 1
</div>
<div id="2" class="section">
Test 2
</div>
<div id="hello-world" class="section">
Test 3
</div>
</div>
Related
My main menu adds 'scroll' class to '.mainMenu' when the offset is 25px from the top. This functions fine, but when a user refreshes the page anywhere besides the top of the page, '.scroll' isn't active, so the 'scroll' styles do not apply.
Use case:
User is on top of page, scrolls down, '.scroll' class is added, turning white background to black.
User refreshes window when on the middle of the page. The main menu turns back into a white background because scroll isn't added.
I'm looking for something like: 'If mainMenu class is 25px from the very top of the page, then add class scroll to the mainMenu div'.
Demo:
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 10) {
$(".mainMenu").addClass("scroll");
} else {
$(".mainMenu").removeClass("scroll");
}
});
.gap{
height: 800px;
}
ul {
list-style-type: none;
display: flex;
flex-direction: row;
}
ul li{
padding: 0px 20px;
}
.mainMenu{
transition: background 0.5s;
}
.mainMenu.scroll {
background:red;
z-index: 9999;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="mainMenu">
<ul>
<li>link 1</li>
<li>link 1</li>
</ul>
</div>
<div class="gap"></div>
Just use the same code you use for the example (use case 1) but with a different event.
Like so:
$(function() { // called when page is refreshed
var scroll = $(window).scrollTop();
if (scroll >= 10) {
$(".mainMenu").addClass("scroll");
} else {
$(".mainMenu").removeClass("scroll");
}
});
I use this nice little JavaScript to make my navigation bar (which is normally sitting 230px down from the top) stick to the top of the page once the page is scrolled down that 230 px. It then gives the "nav" element a "fixed" position.
$(document).ready(function() {
$(window).bind('scroll', function() {
if ($(window).scrollTop() > 230) {
$('nav').addClass('fixed');
} else {
$('nav').removeClass('fixed');
}
});
});
nav {
width: 90%;
display: flex;
justify-content: center;
max-width: 1400px;
height: 85px;
background-color: rgba(249, 241, 228, 1);
margin: auto;
border-top-left-radius: 0em;
border-top-right-radius: 0em;
border-bottom-left-radius: 2em;
border-bottom-right-radius: 2em;
}
.fixed {
position: fixed;
border-top: 0;
top: 0;
margin: auto;
left: 0;
right: 0;
z-index: 4;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav>
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
<li>Four</li>
</ul>
</nav>
Now, the problem: i have positioned the corresponding anchor targets
within the page and have given them some "padding-top" to account for the fixed navbar (about 90px), so that they don't disappear behind the bar when the page jumps to them after clicking.
.anchor {
padding-top: 90px;
}
<a class="anchor" id="three">
This works fine AS LONG AS the navbar is already fixed to the top.
But if you click on a link while the navbar is still in its original mid-page position (e.g. the first click the user will do), it just disregards the offset i gave the anchor target and jumps to a weird position where the anchor target is hidden behind the navbar (and not even aligned with the top of the page)!
If i THEN click on the link again (now in the fixed bar on top of the page), it corrects itself and displays the page as i want to. But that first click always misses - i can't figure out why! Please help
EDIT: WORKING DEMO here: http://www.myway.de/husow/problem/problem.html
1st Add a new class name spacebody to your first div with class="space"
<nav>
...
</nav>
<div class="space spacebody">
</div>
2nd JS use the following should fix your problem:
$(document).ready(function() {
$(window).bind('scroll', function() {
if ($(window).scrollTop() > 230) {
$('nav').addClass('fixed');
$('.spacebody').css('margin-top', '85px');
} else {
$('nav').removeClass('fixed');
$('.spacebody').css('margin-top', '0px');
}
});
});
Reason Why?
because when your nav is not fixed, it has a height of 85px, when you scroll down it has no height which is 0 height. Then everything below move up by 85px causing your to go below the target of ONE or TWO etc. It is not you are missing the first click, it is when the nav are not fixed and the click you will be scroll more down by 85px. If you scroll to top and click you will miss again.
You can easily see this if you change your CSS for nav with background-color: transparent;
With the code above should fix it when you nav become fixed to add a margin-top as 85px to the div below so they keep the same height as you clicked.
I have a mobile optimized website with a fixed header. I was thinking about implementing a disappearing functionality to the header in a similar manner as Chrome for Android.
The gist of the behavior is that when the user scrolls up the header gradually disappears with the content, and then gradually re-appears when scrolling up — unlike this question’s answer where the header appears/disappears completely.
My current header looks like this:
#import url('https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.8/css/materialize.min.css');
#import url('https://fonts.googleapis.com/icon?family=Material+Icons');
header {
position: fixed;
top: 0;
width: 100%;
}
header nav {
box-shadow: none !important;
}
#content {
margin-top: 56px;
padding: 10px;
background: #f5f5f5;
}
<header>
<nav>
<div class="nav-wrapper">
My Awesome Website
<ul id="nav-mobile" class="right">
<li><a><i class="material-icons">settings</i></a>
</li>
</ul>
</div>
</nav>
</header>
<div id="content">
<p class="flow-text">This is where the rest of the website’s content should be</p>
<br><br>
<p>Don’t mind me, just adding a few blank lines to make this scrollable…</p>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
</div>
How can I implement this using jQuery?
The solution is fairly simple: store the scroll position, listen for the scroll event on <body> (or, generally speaking, whatever your topmost scrollable container is) and check if the position has increased or decreased, then perform the appropriate action (increase/decrease the header’s top).
The Math.min and Math.max are necessary to prevent the header from going flying across the screen in either direction. They limit the top value so that it's either hidden just above the viewport or pinned right at the top.
Here’s a working demo:
(function($) {
var $html = $('html'),
$header = $('header'),
lastScrollTop = $html.scrollTop();
var disappearingHeaderHandler = function() {
var scrollTop = $html.scrollTop(),
headerHeight = $header.outerHeight(),
headerTop = parseInt($header.css('top'), 10);
$header.css('top',
scrollTop > lastScrollTop
? Math.max(-headerHeight, headerTop - (scrollTop - lastScrollTop))
: Math.min(0, headerTop + (lastScrollTop - scrollTop))
);
lastScrollTop = scrollTop;
};
$(window).on('scroll', disappearingHeaderHandler);
disappearingHeaderHandler();
})(jQuery);
#import url('https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.8/css/materialize.min.css');
#import url('https://fonts.googleapis.com/icon?family=Material+Icons');
header {
position: fixed;
top: 0;
width: 100%;
}
header nav {
box-shadow: none !important;
}
#content {
margin-top: 56px;
padding: 10px;
background: #f5f5f5;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<header>
<nav>
<div class="nav-wrapper">
My Awesome Website
<ul id="nav-mobile" class="right">
<li><a><i class="material-icons">settings</i></a>
</li>
</ul>
</div>
</nav>
</header>
<div id="content">
<p class="flow-text">This is where the rest of the website’s content should be</p>
<br>
<br>
<p>Don’t mind me, just adding a few blank lines to make this scrollable…</p>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
</div>
el.scrollIntoViewIfNeeded() scrolls to el if it's not inside of the visible browser area. In general it works fine but I'm having problems with using it with a fixed header.
I made an example snippet: (The method doesn't work in Firefox, so neither does the demo) https://jsfiddle.net/ahugp8bq/1/
In the beginning all three colored divs are displayed below the fixed header. But if you click "second" and then "first", the beginning of #first will be behind the header, which I don't want.
The problem seems to be that the position of #otherContainer (its padding-top) is pretty much ignored when scrolling up.
Actually, this is quite simple if you use the consistent and supported getBoundingClientRect().top + body.scrollTop way - all you now have to do is reduce the header from it, so just get it and calculate its height.
var header = document.getElementById('container')
var clicks = document.querySelectorAll('#container li');
var content = document.querySelectorAll('#otherContainer > div');
// Turn the clicks HTML NodeList into an array so we can easily foreach
Array.prototype.slice.call(clicks).forEach(function(element, index){
element.addEventListener('click', function(e){
e.preventDefault();
// Set the scroll to the top of the element (top + scroll) minus the headers height
document.body.scrollTop = content[index].getBoundingClientRect().top + document.body.scrollTop - header.clientHeight;
});
});
#container {
position: fixed;
background: yellow;
width: 100%;
height: 50px;
}
ul li {
display: inline;
cursor: pointer;
}
#otherContainer {
padding-top: 60px
}
#first, #second, #third {
height: 500px
}
#first {
background: red
}
#second {
background: green
}
#third {
background: blue
}
<div id="container">
<ul>
<li id="jumpToFirst">first</li>
<li id="jumpToSecond">second</li>
<li id="jumpToThird">third</li>
</ul>
</div>
<div id="otherContainer">
<div id="first"></div>
<div id="second"></div>
<div id="third"></div>
</div>
I am trying to do scrollTop animation to an anchor that resides inside of a fullscreen <section>, but does not scroll to right anchor at first click. Here's my code.
<nav id="scroller"> Scroll me to sub 1
Scroll me to sub 2
Scroll me to sub 3
</nav>
<section id="boxTop"></section>
<section id="boxMaster">
<section id="subBox1">
<p>Hello. I am the Sub 1!</p>
</section>
<section id="subBox2">
<p>Hello. I am the Sub 2!</p>
</section>
<section id="subBox3">
<p>Hello. I am the Sub 3!</p>
</section>
</section>
$("#scroller a").click(function () {
$('#boxMaster').animate({
scrollTop: $(this.hash).offset().top
}, 700);
$("#scroller a").removeClass("active");
$(this).addClass("active");
});
fiddle
$("#scroller a").click(function() {
$('#boxMaster').animate({
scrollTop: $(this.hash).offset().top
}, 700);
$("#scroller a").removeClass("active");
$(this).addClass("active");
});
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
#scroller {
position: fixed;
top: 0;
height: 30px;
text-align: center;
}
#scroller a {
color: #fff;
margin: 0 20px;
text-decoration: none;
}
#scroller a.active {
font-weight: bold;
text-decoration: underline;
}
#boxTop {
width: 100%;
height: 100%;
background: red;
}
#boxMaster {
width: 100%;
height: 100%;
background: blue;
overflow: hidden;
}
#boxMaster #subBox1,
#boxMaster #subBox2,
#boxMaster #subBox3 {
width: 100%;
height: 100%;
display: table;
}
p {
color: #fff;
text-align: center;
display: table-cell;
vertical-align: middle;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav id="scroller"> Scroll me to sub 1
Scroll me to sub 2
Scroll me to sub 3
</nav>
<section id="boxTop"></section>
<section id="boxMaster">
<section id="subBox1">
<p>Hello. I am the Sub 1!</p>
</section>
<section id="subBox2">
<p>Hello. I am the Sub 2!</p>
</section>
<section id="subBox3">
<p>Hello. I am the Sub 3!</p>
</section>
</section>
Add the current scroller value to the offset().top(), the latter beeing relative to the top of frame, and get rid of this.hash. Use this.href instead.
$("#scroller a").click(function () {
var y=$('#boxMaster').scrollTop()
$('#boxMaster').animate({
scrollTop: $(this.href).offset().top + y
}, 100);
});
You need to scroll the #boxMaster element relative to the link's position within the element and relative to the #boxMaster element's top position within the body element.
You can do this by adding the #boxMaster element's scrollTop() value with its top position, and then subtracting that from the link's offset top value:
$(this.hash).offset().top - $('#boxMaster').position().top + $('#boxMaster').scrollTop()
Updated Example
var $boxMaster = $('#boxMaster');
$boxMaster.animate({
scrollTop: $(this.hash).offset().top - $boxMaster.position().top + $boxMaster.scrollTop()
}, 700);
You may also need to prevent the link element's default behavior using e.preventDefault(), and then manually scroll the html/body element to the #boxMaster element:
Updated Example
$('html, body').animate({
scrollTop: $boxMaster.offset().top
}, 700);
I have a fixed header. I started with w3school's code. However, I've been struggling with this same problem for so long, finally found a workaround for the "first click incorrect" issue:
Just before (outside) of my click event, I simply created a variable "x", initialized:
var x=1;
Then I have a conditional statement inside the click event checking for x:
if (x==1) {
console.log("x is now: " + x);
x=0;
console.log("x is now: " + x);
jQuery("html, body").animate({
scrollTop: jQuery("div.class-of-element-i-am-scrolling-to" + hash).position().top - jQuery("div.header-container").outerHeight(true) - jQuery("h3.another-element-in-my-way").outerHeight(true)
}, 2000, function(){
return false;
});
} else {
jQuery("html, body").animate({
scrollTop: jQuery("div.class-of-element-i-am-scrolling-to" + hash).position().top
}, 2000, function(){
return false;
});
}
In other words, using "x" as a flag to check if it is the first time the code is run.
If it is, then I am kind of cheating by subtracting the fixed header and the other elements that are pulling my desired div up. Remember to make "x=0" to "drop" the flag.
If it isn't, then it works fine anyway.