Sticky Navigation Bar Jumps on Scroll (JS, HTML, CSS) - javascript

I am building this site as a fun little side project but I am stuck. My sticky nav bar jumps once the user scrolls far enough down. I have read other threads and can't quite connect the dots.
I have been thinking it must be a padding issue, however, my JS isn't all that great so there is potential for problems there as well.
Here is my Javascript:
var header = document.getElementById("header");
var navbar = document.getElementById("navbar");
var navbarHeight = navbar.offsetHeight;
var headerHeight = header.offsetHeight;
header.style.height = screen.height - navbarHeight;
function initJake() {
if (window.pageYOffset > headerHeight) {
navbar.style.position = "fixed";
navbar.style.top = "0";
} else {
navbar.style.position = "relative";
}
}
window.onscroll = function() {
initJake()
};
Here is my jsFiddle (the links are cut off since this is the full-screen HTML setup): https://jsfiddle.net/jihlenfeldt/435ugdyf/2/
I am hoping to find a way in which the transition from absolute to fixed is smooth and doesn't end up covering a bunch of lines of text.
Thank you to anyone willing to offer a bit of advice, this little issue has become quite a headache.

Is this what you mean?:
var header = document.getElementById("header");
var navbar = document.getElementById("navbar");
var content = document.querySelector('#navbar + .content');
var navbarHeight = navbar.offsetHeight;
var headerHeight = header.offsetHeight;
header.style.height = screen.height-navbarHeight;
function initJake(){
if(window.pageYOffset > headerHeight){
navbar.style.position = "fixed";
navbar.style.top = "0";
content.style.padding = '60px 0 0 0';
}
else{
navbar.style.position = "relative";
content.style.padding = '0 0 0 0';
}
}
function hamburgerMenu() {
var x = document.getElementById("submenu");
if (x.style.display == "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
}
window.onscroll = function() {initJake()};
The issue here is that your navbar gets position "fixed" and its height is not considered by the DOM anymore since it'll be positioned "on top" of everything. Best way to fix this is to give the element after it (the content in this case) a padding-top of the same height that navbar has. (Unlike me, use a variable that gets the height of the navbar since its height can vary, not a fixed number like I did (the 60px))

Well here's your mistake.
if (window.pageYOffset > headerHeight) {
navbar.style.position = "fixed";
navbar.style.top = "0";
} else {
navbar.style.position = "relative"; // here
}
Your navbar has position absolute and when window.pageYOffset > headerHeight is false, you are making it relative
#navbar {
position: absolute;
bottom: 0;
width: 100%;
background-color: #111;
z-index: 3;
overflow: hidden;
height: 20%;
padding-bottom: 3%;
display: flex;
flex-direction: column;
transition-property: width;
}
That's why the bump. Also, the navbar is fixed now, so It doesn't disturbs other elements. That's why the this div is covered by the navbar (or is going upwards)
<div class="content">
<h1>text here</h1>
<p>text here text here text here text here text here text here text here text here text here text here text here text here </p>
</div>
Possible fix is we stick to one position. Let's make it relative. Then we add margin top to the content div when the position is fixed. Which makes it some distance from above. So javascript becomes becomes
var content = document.getElementsByClassName("content")[0];
if (window.pageYOffset > headerHeight) {
navbar.style.position = "fixed";
navbar.style.top = "0";
content.style.marginTop = " 115px";
} else {
navbar.style.position = "relative";
content.style.marginTop = "0px";
}
And stick to relative in css
#navbar {
position: relative;
...
}
So your fiddle becomes something like this

Why do you guys use always JS to interact with the appearance of a HTML object.
Use JS to check if the header is outside of the viewport and if yes, set a class on the body. Via CSS you can modify the sticky header.
Something like this:
$(window).scroll(function(){
if($(this).scrollTop() > $('#header').outerHeight()){
$('body').addClass('scrolled');
} else {
$('body').removeClass('scrolled');
}
});
And via CSS
#header { position:relative; }
body.scrolled #header { position:fixed; top:0; left:0; width:100%; }
body.scrolled { padding-top:<Enter here Height of Header to prevent jumping> }

Related

Setting <div> size to be the same as window?

I'm trying to use jQuery to set the height of a div so that it takes up the entire window + the height of a header (so that you can scroll the header off the page) but no more than that. I would think the height of the div would be the height of the window + the height of the header I'm trying to hide.
When I set the div to window height, however, it creates overflow. Here's the rough code:
var $body = $("#body"),
$container = $("#container"),
$window = $(window),
$content = $("#mainContent"),
$header = $("#header"),
bodyHeight = window.innerHeight + $header.height();
$body.css("height", window.innerHeight);
$container.css("height", bodyHeight);
div {
display: block;
width: 100%;
margin: 0;
}
#body {
overflow-y: scroll;
}
#container {
overflow-y: hidden;
}
#header {
overflow: hidden;
}
#navbar {
height: 10px;
background-color: brown;
}
#mainContent {
height: 200px;
overflow-y: scroll;
}
#contentP {
height: 400px
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="body">
<div id="container">
<div id="header">
<h1>Header</h1>
</div>
<div id="navbar">
</div>
<div id="mainContent">
<p id="contentP">This is content</p>
</div>
</div>
</div>
Why is there overflow if the div is sized to fit in the window?
EDIT: So far, answers haven't helped. This is the site I'm working on. It's joomla. I want the nav bar to lock at the top of the screen.
$(document).ready(function() {
//Declare some variables
var $window = $(window),
$body = $(".body"),
$mainContent = $("#maincontent"),
headerGap = parseFloat($("#headerimg").css("margin-top")),
headerHeight = headerGap + $("#header").height() + parseFloat($("#navbar").css("margin-top")),
navbarHeight = $("#navbar").height(),
footerHeight = $("#footer").height();
//set the height of the body and the maincontent
resizePage();
//Set the listeners for resizing and scrolling
$window.resize(resizePage);
$window.scroll(scrollHandler);
//When you scroll, see if the navbar is at the top. Set maincontent overflow
//to scroll when the navbar is at the top of the window. Set it to hidden otherwise
function scrollHandler() {
if ($window.scrollTop() < headerHeight - 1) {
$mainContent.css("overflow", "hidden");
} else {
$mainContent.css("overflow", "auto");
}
}
//Set the body and the mainContent to be the correct sizes when the window size is changed. In theory, the body should be:
// windowHeight + headerHeight
// maincontent should be:
// windowHeight - (headerHeight + navbarHeight + footerHeight)
// But that doesn't quite work out.
function resizePage() {
//Deal with the changing CSS due to media queries
if ($(window).width() > 768) {
headerGap = parseFloat($("#headerimg").css("margin-top"));
headerHeight = headerGap + $("#header").height() + parseFloat($("#navbar").css("margin-top")) - 1;
$(".nav.menu.nav-pills").css("width", "92.5%");
}
else {
headerHeight = $("#header").height();
$(".nav.menu.nav-pills").css("width", $window.width());
}
//The header and navbar height change at certain sizes, so grab them again to be safe.
navbarHeight = $("#navbar").height();
footerHeight = $("#footer").height();
var windowHeight = $window.height(),
contentHeight = windowHeight - (footerHeight + navbarHeight);
//if we account for headerHeight too, maincontent is too big
resizeContent(contentHeight);
resizeBody(windowHeight);
}
//The body should take up the whole height of the window, plus the header
//and margin heights at the top. This way, you scroll to the navbar.
// But it doesn't work this way.
// -7 and -27 are from eyeballing it.
function resizeBody(windowHeight) {
if($window.width() > 728) {
$body.css("height", windowHeight - 7);
}
else {
$body.css("height", windowHeight - 27);
}
}
// The content should go from the bottom of the navbar to the bottom of the footer.
//
function resizeContent(contentHeight) {
$mainContent.css("top", (headerHeight + navbarHeight));
$mainContent.css("bottom", (0 - headerHeight));
//For the background slideshow on the Furniture page
// Again, + 5 was eyeballed
$("div.moduletable").css("height", contentHeight + 5);
if ( (contentHeight + 5) < ($(window).width()) /2 ) {
$(".wk-slideshow img").css("width", "100%");
$(".wk-slideshow img").css("height", "auto");
}
else {
$(".wk-slideshow img").css("height", contentHeight + 5);
$(".wk-slideshow img").css("width", "auto");
}
}
});
It works for a lot of sizes, but one you get to small resolutions it falls apart.
EDIT 2: I was able to get the effect I was going for by adding another div. I set the body to be the height of the window and the new div to be the size of the body + the height of the header. The body has "overflow-y: scroll". The container would have "overflow-y: hidden" (See updated snippet). This doesn't totally answer my question, but at least it helps?
I've taken a look at your code and altered it. Try this and see if this is what you're looking for.
In my example i'm looking for the element by getElementById and then I set it's style.height to window.innerHeight - 10px without taking the 10px it wouldn't show the border fully on the page. So you just remove 10px's. The example has been tested on different screen sizes.
Javascript example:
function autoResizeDiv() {
document.getElementById('body').style.height = window.innerHeight - 10 + 'px';
console.log(window.innerHeight - 10 + 'px');
}
window.onresize = autoResizeDiv;
autoResizeDiv();
#body {
display: block;
border: 5px solid black;
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="body">
</div>
The following worked for me:
$(".body").height($(window).height());
I figured out the biggest problem. I was using some absolutely positioned elements without giving a parent any other position. This made things show up wonky when I was trying to size other things. I also needed to have an extra div as a container for all the content on the page that would be the height of the window + the height of the header.
Thanks to everyone who answered, it helped!

Navbar makes content jump when position is changed to fixed on scroll past

I've been trying to make my navbar stick to top when I scroll by it and achieved it. The only problem is that my content kind of kicks up when the navbar transition to position fixed is executed.
Here is an example of this behavior: http://jsfiddle.net/7HHa5/4/
JavaScript
window.onscroll = changePos;
function changePos() {
var header = document.getElementById("header");
if (window.pageYOffset > 70) {
header.style.position = "absolute";
header.style.top = pageYOffset + "px";
} else {
header.style.position = "";
header.style.top = "";
}
}
I am using bootstrap and jQuery.
How can I avoid this behavior?
When you set the header to position: absolute, it leaves an empty space which gets filled by the content. You need to add a margin to the top of the content when the header becomes fixed, like this:
window.onscroll = changePos;
function changePos() {
var header = document.getElementById("header");
var content = document.getElementById("content");
if (window.pageYOffset > 70) {
header.style.position = "absolute";
header.style.top = pageYOffset + "px";
content.style.marginTop = '55px'
} else {
header.style.position = "";
header.style.top = "";
content.style.marginTop = '0'
}
}
See http://jsfiddle.net/2EhLs/1/ for an example.
However, there is a better way.
Since you are already using Bootstrap, you should consider using the built-in Affix feature.
The best example is this one from another question:
$('#nav-wrapper').height($("#nav").height());
$('#nav').affix({
offset: { top: $('#nav').offset().top }
});

Dynamically show/hide back to top button with javascript

I'm having a hard time finding a Javascript piece of code to dynamically show the Back to Top button when the user has scrolled, lets say, more than 1000 pixels. All examples use jQuery, and I can't use jQuery. Any help will be very appreciated.
Set the CSS when pageOffset is a certain point (in a window.onscroll event):
window.onscroll = function()
{
if(pageOffset >= 1000)
{
document.getElementById('backToTopID').style.visibility="visible"
}
};
something more full would be:
window.onscroll = function()
{
if(pageOffset >= 1000)
{
document.getElementById('backToTopID').style.visibility="visible"
}else
{
document.getElementById('backToTop').style.visibility="hidden";
}
};
DEMO
JavaScript using Window.onscroll
var appended = false, bookmark = document.createElement("div");
bookmark.id = "arrowUp";
bookmark.innerHTML = "<a href=\"#\" title=\"Top of the page.\">↑<\/a>";
onscroll = function() {
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
if (scrollTop > 500) {
if (!appended) {
document.body.appendChild(bookmark);
appended = true;
}
} else {
if (appended) {
document.body.removeChild(bookmark);
appended = false;
}
}
};
source
https://developer.mozilla.org/en-US/docs/Web/API/window.onscroll
demo link
http://jsfiddle.net/MA4dC/
This is how I do it. To show back to top button when user scrolls more than 150 pixels down from top of document.
//how to show back to top button when user scrolls more than 150 pixels down from top of document.
var toTopButton = document.querySelector("a");
toTopButton.style.display = "none";//by default should be hidden
document.querySelector('body').onscroll = function(){//whenever they scroll
if (window.scrollY > 150)//if scroll is 150px from top
toTopButton.style.display = "block";//if they scroll down, show
else
toTopButton.style.display = "none";//if they scroll up, hide
};
html {scroll-behavior: smooth;}
a {
background-color: #f00;
position: fixed;
bottom: 10px;
right: 10px;
}
<html>
back to top
<body id="top">
text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>
</body>
<html>
OR to show back to top button when user scrolls more than 150 pixels up from bottom of document.
//how to show back to top button when user scrolls more than 150 pixels up from bottom of document.
var toTopButton = document.querySelector("a");
toTopButton.style.display = "none";
document.querySelector('body').onscroll = function(){
if (window.innerHeight + 150 < document.body.offsetHeight)//if document long enough
if (window.scrollY + window.innerHeight > document.body.offsetHeight - 150)//if scroll is 150px from bottom (if 'bottom of what we are looking at' is > than 'bottom of document - 150px earlier)
toTopButton.style.display = "block";
else
toTopButton.style.display = "none";
};
html {scroll-behavior: smooth;}
a {
background-color: #f00;
position: fixed;
bottom: 10px;
right: 10px;
}
<html>
back to top
<body id="top">
text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>text<br>
</body>
<html>
Simple but working.
CSS:
#scrollToTop { visibility: hidden; }
JavaScript:
// Show/Hide the button
window.onscroll = function() {
var pageOffset = document.documentElement.scrollTop || document.body.scrollTop,
btn = document.getElementById('scrollToTop');
if (btn) btn.style.visibility = pageOffset > 450 ? 'visible' : 'hidden';
};

Is it possible to proof an div element of visibility inside a "overflow-x:scroll" div-container ?

I am currently trying to figure out how to get the information of how much percent is visbile of a div inside a overflow-x:scroll container. I also need to know if it comes from the right or from the left ? Is this possible somehow ?
This might be helpfull. You could edit the code from the answer to that question to check if the element was scrolled in from the right or the left.
Check if element is visible after scrolling
To calculate the percentage visable you just need to compare child's size to the parent's size and what the 'left' offset of the child is.
(I might add a code example later)
EDIT
I made a small example that show how you can detect the visible percentage of that child inside an "overflow-x:scroll" div-container and if the child comes from left or right.
<style>
#parent {
overflow-x:scroll;
width: 300px;
height:120px;
border: solid 1px #000;
}
#child {
width: 200px;
height: 100px;
background:#FF0;
margin-left: 200px;
}
#scrollPane {
width: 800px;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>
$(document).ready(function(){
$("#button").click(function(){
alert(percantageVisible() + "% of the child is visible");
});
});
function percantageVisible()
{
var parent = $("#parent");
var parentLeftOffset = parent.offset().left;
var parentRightOffset = parentLeftOffset + parent.width();
var child = $("#child");
var childLeftOffset = child.offset().left;
var childRightOffset = childLeftOffset + child.width();
if(childLeftOffset < parentLeftOffset && childRightOffset > parentLeftOffset && childRightOffset < parentRightOffset){
// percentage from the left
var width = child.width();
var hiddenWidth = Math.abs(childLeftOffset - parentLeftOffset);
var visibleWidth = width - hiddenWidth;
return visibleWidth/(width/100);
}
else if(childRightOffset > parentRightOffset && childLeftOffset < parentRightOffset && childLeftOffset > parentLeftOffset ){
// percentage from the right
var width = child.width();
var hiddenWidth = Math.abs(parentRightOffset -childRightOffset);
var visibleWidth = width - hiddenWidth;
return visibleWidth/(width/100);
}
else if (childLeftOffset > parentLeftOffset && childRightOffset < parentRightOffset){
// all visible
return 100;
}
else{
// invisible
return 0;
}
}
</script>
<div id="parent">
<div id="scrollPane">
<div id="child"> </div>
</div>
</div>
<button id="button">check percentage</button>
Hope this helps

Why does an element start its css3 transition/transform from the top left of the window?

This is a bit annoying: i have a div which starts its transition from the top left of the window even when positioned anywhere else on the document. I've tried usign -webkit-transform-origin with no success, maybe i've used it wrong.
Could anybody help me? :)
Here's the code... all of it, but i've commented on the relevant parts - which are at the bottom, mainly.
Here's a live version of the code.
<style>
#pool{
width:100%;
}
.clickable{
<!-- class of the element being transitioned -->
display:inline;
margin-right: 5px;
-webkit-transition: all 500ms ease;
position: absolute;
}
.profile_image{
border: solid 1px black;
-webkit-transition: all 500ms ease;
position:relative;
}
</style>
<section id="pool"></section>
<script>
var Cache = {};
Cache.hasItem = function(item_key){
if(!localStorage.getItem(item_key)){
return false;
}else{
return true;
}
}
Cache.storeItem = function(key, value){
localStorage.setItem(key, value);
}
Cache.fetch = function(key){
return jQuery.parseJSON(localStorage.getItem(key));
}
Cache.clear = function(key){
localStorage.removeItem(key);
}
var Twitter = {};
Twitter.url = "http://api.twitter.com/1/statuses/public_timeline.json?callback=?";
Twitter.getFeed = function(){
console.log("Fetching...");
$.getJSON(Twitter.url, function(json){
Cache.storeItem('feed',JSON.stringify(json));
})
.complete(function(){
//to be implemented
console.log("Completed");
})
}
if(!Cache.hasItem('feed')){
Twitter.getFeed();
}
var feed = Cache.fetch('feed');
for(var i in feed){
var entry = feed[i];
var element = '<div id="'+i+'" class="clickable"><img class="profile_image" src="'+entry.user.profile_image_url+'"/></div>';
$("#pool").append(element);
}
</script>
<script>
$(".profile_image").click(function(e){
var target = $(e.target);
var parent = target.parent();
var windowWidth = $(window).width();
var windowHeight = $(window).height();
var newWidth = 500;
var newHeight = 100;
var newX = (windowWidth-newWidth)/2;
var newY = (windowHeight-newHeight)/3;
/************HERE'S THE PROBLEM*******/
parent.css('background-color','red');
parent.css('display','inline');
parent.css('position','fixed'); // tried absolute and inherit as well
parent.css('z-index','3');
parent.width(newWidth).height(newHeight).offset({top:newY, left:newX});
})
</script>
Results:
With help from jfriend00 i managed to fix it. Here's the code:
<style>
#pool{
width:100%;
display: inline;
}
.clickable{
display: inline-block;
margin-right: 5px;
position: scroll;
}
.profile_image{
border: solid 1px black;
}
</style>
And the Javascript:
<script>
$(".profile_image").click(function(e){
var target = $(e.target);
var parent = target.parent();
targetOffset = target.offset();
parentOffset = parent.offset();
target.css('top',targetOffset.top-5);
target.css('left',targetOffset.left-5);
parent.css('top',parentOffset.top);
parent.css('left',parentOffset.left);
var windowWidth = $(window).width();
var windowHeight = $(window).height();
var newWidth = 500;
var newHeight = 100;
var newX = (windowWidth-newWidth)/2;
var newY = (windowHeight-newHeight)/3;
parent.css('-webkit-transition', 'all 500ms ease');
parent.css('background-color','red');
parent.css('position','absolute');
parent.css('z-index','3');
parent.width(newWidth).height(newHeight).offset({top:newY, left:newX});
})
</script>
It looks to me like you change the object's position from relative to fixed upon the click (along with a few other style changes). When you change it to fixed, the object is no longer positioned in the flow of the page and it goes to it's left and top position on the page which it does not look like you've initialized - thus they are set to (0,0) so that's where the object jumps to when you change it's position to fixed (top/left of the page).
If you want them to transition from where they were, you will have to calculate their original position on the page and set top and left to those values in the same code where you set the position to fixed.
I would assume that jQuery has a function to calculate the object's absolute position in the page for you so you can use that (YUI has such a function so I assume jQuery probably does too). Since you're using "fixed", you may have to correct that for scroll position or use "absolute" instead of "fixed". One challenge here is you need to change the position and top/left without them being subject to a CSS transition because you want those attributes to change immediately. Then, you enable the transitions and set the final position.

Categories