Dynamically positioning element on scroll - javascript

Goal
To have the page navigation positioned lower on the page when initially loaded. So that it looks like pictured below.
Background
I created a navigational element that is using Headroom.js to control its position. The point of the library is that it moves the desired navigational item out of view when a user is scrolling down so that you can see more content. Then the item shows up when you scroll back up to make it convenient to click on a link if that is what you needed to do.
Current State
I have this current demo on codepen.
That navigational item is at the top of the page but on a lower z-index. So not initially visible.
when you scroll down the element is out of view.
But when you scroll up, it is where it needs to be
Code
HTML
<nav id="page-menu" class="link-header header--fixed slide slide--reset" role="banner">
<ul>
<li>Products</li>
<li>Features</li>
<li>Testimonials</li>
<li>Cases</li>
</ul>
</nav>
CSS
#page-menu {
background-color: #BA222B;
list-style-type: none;
width: 100%;
z-index:10;
}
#page-menu ul {
list-style-type: none;
margin: 0;
position: absolute;
bottom: 5px;
right: 10px;
}
#page-menu ul li {
display: inline-block;
margin-left: 10px;
}
#page-menu ul li a {
text-decoration: none;
color: #fff;
}
.link-header {
background-color:#292f36;
height: 100px;
}
.header--fixed {
position:fixed;
z-index:10;
right:0;
left:0;
top:0px;
}
jQuery
(function() {
new Headroom(document.querySelector("#page-menu"), {
tolerance: 5,
offset : 150,
classes: {
initial: "slide",
pinned: "slide--reset",
unpinned: "slide--up"
}
}).init();
}());
Full demo on codepen.

Goal :
From what you are describing, you want the read navigation to appear as such on page load:
And move with the gray bar, but and down, as the user scrolls, until it cutoff point reaches the bottom of the gray bar. Then you want things to kick in, and have the red bar slide up and out of view, and then up and down depending on scroll. You want the transition to be smooth.
Method:
The thing to keep in mind for a smooth transition is that you have two states: A top state and a bottom state. You have to design both, you have to figure out the exact height to change over, and you have to make sure that they will be identical at that spot, so appear seamless.
Top State:
We don't need any sort of extra positioning here. We want it to be static in fact, as odd as that might sound.
Bottom State:
We want fixed positioning here. Since we want the changeover to occur right when the red bar touches the top of the window, your CSS in fixed-header is perfect already.
Changeover Height:
The header and the gray nav bar combined are 180px, so that number will be our change over.
Code:
1. Statechange
Lets work backwards and take the state change first. You will need to change from 150px to 180px in a lot of places. For example, your JS code:
Existing JS:
if ($(window).scrollTop() >= 150) {
...
(function() {
new Headroom(document.querySelector("#page-menu"), {
tolerance: 5,
offset : 150,
New JS:
if ($(window).scrollTop() >= 180) {
...
(function() {
new Headroom(document.querySelector("#page-menu"), {
tolerance: 5,
offset : 180,
And your header will need an updated height, or a removal of height entirely.
Existing CSS:
header {
height:150px;
position: relative;
z-index:30;
}
New CSS:
header {
position: relative;
z-index:30;
}
2. Top State
The big thing here messing you up is that for some reason the library you are using is applying .header--fixed and link-header on page load. I don't know how to prevent this, but we can just neutralize is by removing them from your CSS.
Remove This CSS:
.link-header {
background-color:#292f36;
height: 100px;
}
.header--fixed {
position:fixed;
z-index:10;
right:0;
left:0;
top:0px;
}
Second, we need to tweak the ul inside your red nav.
Existing CSS:
#page-menu ul {
list-style-type: none;
margin: 0;
position: absolute;
bottom: 5px;
right: 10px;
}
New CSS:
#page-menu ul {
list-style-type: none;
margin: 0 auto;
padding:0;
width:960px;
max-width:100%;
text-align:right;
}
3. Bottom State
Everything works really well here aleady, except that the fixed-header class is getting added to the gray nav as well. We need to tweak our jQuery selector bit.
Existing JS:
if ($(window).scrollTop() >= 180) {
$('nav#page-menu').addClass('fixed-header');
}
else {
$('nav#page-menu').removeClass('fixed-header');
}
NewJS:
if ($(window).scrollTop() >= 180) {
$('header nav').addClass('fixed-header');
}
else {
$('header nav').removeClass('fixed-header');
}
4. Misc Cleanup
Everything looks really good here, except that the lis inside our two navs don't line up. We need to fix some margin-right to bring them into line.
Existing CSS:
#page-menu ul li {
display: inline-block;
margin-left: 10px;
}
New CSS:
#page-menu ul li {
display: inline-block;
margin-left: 10px;
margin-right: 10px;
}
Finally, I noticed that there's a missing closing bracket in your HTML, in the gray nav. It's not hurting much, but it could:
<nav>
<ul>
<li>Dentists</li>
<li>Labs</li>
<li>Patients</li>
<ul> <--- ( Should be </ul> )
</nav>
End Result:
http://codepen.io/anon/pen/qIrhx

Related

Scrolling Nav Sticks to Top

My problem is along the lines of these previous issues on StackOverflow but with a slight difference.
Previous issues:
Stopping fixed position scrolling at a certain point?
Sticky subnav when scrolling past, breaks on resize
I have a sub nav that starts at a certain position in the page. When the page is scrolled the sub nav needs to stop 127px from the top. Most of the solutions I have found need you to specify the 'y' position of the sub nav first. The problem with this is that my sub nav will be starting from different positions on different pages.
This is the JS code i'm currently using. This works fine for one page but not all. Plus on mobile the values would be different again.
var num = 660; //number of pixels before modifying styles
$(window).bind('scroll', function () {
if ($(window).scrollTop() > num) {
$('.menu').addClass('fixed');
} else {
$('.menu').removeClass('fixed');
}
});
I'm looking for a solution that stops the sub nav 127px from the top no matter where on the page it started from.
You can use position: sticky and set the top of the sub-nav to 127px.
See example below:
body {
margin: 0;
}
.main-nav {
width: 100%;
height: 100px;
background-color: lime;
position: sticky;
top: 0;
}
.sub-nav {
position: sticky;
width: 100%;
height: 50px;
background-color: red;
top: 100px;
}
.contents {
width: 100%;
height: 100vh;
background-color: black;
color: white;
}
.contents p {
margin: 0;
}
<nav class="main-nav">Main-nav</nav>
<div class="contents">
<p>Contents</p>
</div>
<nav class="sub-nav">Sub-nav</nav>
<div class="contents">
<p>More contents</p>
</div>
Please see browser support for sticky here
You should change your code to the below, should work fine:
$(window).bind('scroll', function () {
if ($(window).scrollTop() > $(".menu").offset().top) {
$('.menu').addClass('fixed');
} else {
$('.menu').removeClass('fixed');
}
});
Maybe you can try this:
Find navigation div (.menu)
Find the top value of the .menu (vanilla JS would be menuVar.getBoundingClientRect().top, not sure how jQuery does this).
Get top value of browserscreen.
Calculate the difference - 127px.
When the user scrolls and reaches the top value of the menu -127px -> addClass('fixed').

In-page anchors not working properly in combination with "scroll-then-fix" JS navbar code

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.

How to make this working Java-script more efficient

I've got a working jQuery script that runs ok meaning it serves its purpose.
The question is: how to make this script more efficient?
Currently the script becomes active the moment a user places the mouse over (hover) a certain HTML5 section-tag with an ID. At this moment the script removes the existing class named 'noDisplay' from a subordinate nav-tag containing a submenu list, hence content becomes visible to the user. This submenu list may be three to four levels deep. The submenus are held in classes (subMenu1, subMenu2, subMenu3, subMenu4, etc.).
The script is written to serve individually each of the given section IDs and its sublevel classes.
Basically the script interacts with the DOM by removing the class 'noDisplay' upon mouse hover and restores the same class upon mouse leave.
(Tried to give a clear explanation. If not please ask.)
Here is a JSfiddle: enter link description here
I hope someone can suggest a way to do this much more efficiently.
Possibly with more sections (#ID's) and subMenu-levels (a class per level).
Using the CSS properties 'display: none;' and 'display:block;' would be the simplest solution but this is not desired because a search-bot my decide to skip content flagged as invisible to the user or a screenreader. The class 'NoDisplay' in use here keeps content invisible to users and keeps its readability to screen readers (and thus to most of the search bots).
So basically the script function remains as is to remove and add the class 'noDisplay' upon hover.
The goal is to obtain a script that is more efficient that could use for instance variables for each section, instead of writing code for each new section and hence extending the current script.
//section1$("#section1 .NavUL1 .subMenu1").hover(function(){
$(".NavUL2").removeClass("noDisplay"); //display
},function(){
$(".NavUL2").addClass("noDisplay"); //no display
});
$("#section1").hover(function(){
$("#section1 .NavUL1").removeClass("noDisplay"); //display
},function(){
$("#section1 .NavUL1").addClass("noDisplay"); //no display
});
$("#section1 .NavUL1 .subMenu1").hover(function(){
$(".NavUL2").removeClass("noDisplay"); //display
},function(){
$(".NavUL2").addClass("noDisplay"); //no display
});
//#section2
$("#section2").hover(function(){
$("#section2 .NavUL1").removeClass("noDisplay"); //display
},function(){
$("#section2 .NavUL1").addClass("noDisplay"); //no display
});
$("#section2 .subMenu1").hover(function(){
$(".subMenu1 .NavUL2").removeClass("noDisplay"); //display
},function(){
$(".subMenu1 .NavUL2").addClass("noDisplay"); //no display
});
$("#section2 .subMenu2").hover(function(){
$(".subMenu2 .NavUL2").removeClass("noDisplay"); //display
},function(){
$(".subMenu2 .NavUL2").addClass("noDisplay"); //no display
});
$("#section2 .subMenu3").hover(function(){
$(".subMenu3 .NavUL2").removeClass("noDisplay"); //display
},function(){
$(".subMenu3 .NavUL2").addClass("noDisplay"); //no display
});
$("#section2 .subMenu4").hover(function(){
$(".subMenu4 .NavUL2").removeClass("noDisplay"); //display
},function(){
$(".subMenu4 .NavUL2").addClass("noDisplay"); //no display
});
My suggestion would be to create a new class, call it whatever but for demonstrative purposes we'll call it hover-class
Then it becomes simple:
$('.hover-class').hover(
function() { $(this).addClass('noDisplay'); },
function() { $(this).removeClass('noDisplay'); }
);
I'd recommend just using CSS, there shouldn't be a need for JS:
nav ul{
position: absolute;
border: 1px solid #444444;
box-shadow: 8px 8px 11px #222222;
background: #888;
padding: 0.5em 0.5em 0.5em 0em;
list-style-type: none;
margin-left: 15%;
display: none;
}
.sectionBox:hover nav > ul, nav li:hover > ul {
display: block;
}
This does away with all the IDs and classes while keeping the same effect. You html looks like this now (just a snippet):
<ul>
<li><h2>various whatever1</h2></li>
<li>link11</li>
<li>link12</li>
<li>link13</li>
<li>link14</li>
<li><h2>sub1</h2>
<ul>
<li>sub1-link11</li>
<li>sub1-link12</li>
<li>sub1-link13</li>
<li>sub1-link14</li>
</ul>
</li>
</ul>
Here it is working: http://jsfiddle.net/VGXNz/1/
Update:
If you want to use your original noDisplay styles then this would be the CSS:
nav ul{
position:absolute;
border: 0;
clip: rect(0 0 0 0);
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
}
.sectionBox:hover nav > ul, nav li:hover > ul{
height: auto;
width: auto;
margin: 0 0 0 15%;
border:1px solid #444444;
box-shadow:8px 8px 11px #222222;
background:#888;
padding:0.5em 0.5em 0.5em 0em;
list-style-type:none;
clip: auto;
overflow: visible;
}
Here's a fiddle: http://jsfiddle.net/KKmVU/1/
why would you use js in the first place? Css is perfectly capable of handling hover states, and IMO you should always go for the css solution if there is one.
I made some quick (and dirty) changes to your fiddle:http://jsfiddle.net/3epRN/1/
I removed a bunch of classes and id's from the markup, removed all js, and tweaked the css a bit. The relevant css looks like this:
.sectionBox nav {
display: none;
}
.sectionBox:hover nav {
display: block;
position: absolute;
top: 90%;
left: 50px;
background-color:#646464;
z-index: 5;
}
.sectionBox nav ul ul {
display: none;
}
.sectionBox nav ul li {
position: relative;
}
.sectionBox nav ul li:hover ul {
display: block;
position: absolute;
top: 0;
left: 80%;
background-color:#646464;
z-index: 5;
}
Obviously this needs some finetuning, but I'm sure you get the idea...
edit
I must admit I missed the part about the display:none beeing a problem for you. I do have to say I disagree with your arguments as to why (it is used al over the net, and crawlers and screen readers are smart enough nowadays).
That beeing said, nothing prevents you to use the css styling you now use to hide content (by adding the noDisplay class) directly in your css where I used the display:none;, and countering it when you want to display content by adding the following in stead of an ordinary display:block:
height: auto;
width: auto;
clip: auto;
overflow: visible;
The result would be identical to your js solution. I updated my fiddle to demonstrate:
http://jsfiddle.net/3epRN/2/

Display only one list item at the center of the <div> with overflow:hidden

I'm trying out a vertical ticker that displays a few text list items one after the other, but I need some help in positioning them.
You'll see a web ticker with two items. To display only one item at a time, I have to set 'overflow:hidden' in #tickerContainer.
However, the text in the ticker is not being positioned at the center of the ticker(As you see it is sitting at the bottom).
Also, when I remove 'overflow:hidden' from #tickerContainer, which is the whole ticker moving away from the top of the page?
Please help me fix this.
http://jsfiddle.net/nodovitt/NYhY4/2/
<div id="tickerContainer">
<ul id="ticker" class="js-hidden">
<li class="news-item">Item Number 1</li>
<li class="news-item">Item Number 2</li>
</ul>
</div>
The jQuery function:
<script>
function tick() {
$('#ticker li:first').slideUp(1000, function () {
$(this).appendTo($('#ticker')).slideDown(1000);
});
}
setInterval(function () {
tick()
}, 2000);
</script>
The CSS:
#tickerContainer {
background-color:white;
border-radius:15px;
text-align:center;
margin:10px;
box-shadow:0 0 8px black;
color:#2B7CD8;
font-size:50px;
width:500px;
height:100px;
overflow:hidden;
}
.news-item {
font-family:Times New Roman;
font-style:oblique;
}
#ticker li {
list-style-type:none;
}
You haven't put any specifications on your ticker id. So something like this http://jsfiddle.net/NYhY4/10/
#ticker {
padding:0px;
margin:0px;
padding-top:20px;
height:55px;
overflow:hidden;
}
Here is my answer as requested by OP
Add this css to your #ticker
#ticker {
margin: 0;
padding: 0;
line-height: 100px;
}
NOTE The line-height will always have to be the height of the #tickerContainer
You can see it here http://jsfiddle.net/NYhY4/3/
#ticker li {
list-style-type:none;
position: relative;
bottom:20px;
}
I didn't like the current item being hidden by seemingly "nothing" (the blank part of li box above the text) so I used this approach:
Remove the margins from the '#ticker'
set '#ticker' padding-top (.5em worked best for me)
set '#ticker li' padding-bottom to push the next item out of view (I used '1em' to be safe but '.5em' worked too)
The words didn't look perfectly centered so
set '#ticker li' line-height to '1em'
#ticker {
margin: 0;
padding-top: .5em;
}
#ticker li{
line-height: 1em;
padding-bottom: 1em;
list-style-type: none;
}
http://jsfiddle.net/JvuKU/2/
Note This depends on the font-size of the '#tickerContainer'. If you change the height or font-size of the '#tickerContainer', just adjust the values of '#ticker' padding-top and '#ticker li' padding-bottom.

SuperScrollorama: Reversing animation

This is my first post, but I'm a long-time viewer. Hope someone can help - this one's been driving me crazy and I've tried and tried to find an answer but to no avail.
Basically, I'm updating a friend's website with a few stylistic scrolling elements. I chose SuperScrollorama as it looks completely amazing, although I'm ready to accept that it's complete overkill for what I'm trying to achieve. I'm really interested in SuperScrollorama anyway, so I suppose I partly chose it just so I could try to use it!
Anyway, the idea I'm trying to implement involves:
Single page website, where section one is simply a centred, large (650px width) image and a navigation menu <ul> centred and fixed to the top of the page containing 6 <li> elements.
On scrolling down, I want the image to shrink to 250px width, and I want to make a space of 250px between the third and fourth <li> for the shrunken image to scroll into and then remain for the duration of the scroll. I'm trying to create the space by adding a margin-right to the third <li>. (I appreciate this may give me undesirable side-effects, one being that it won't be completely centred. Suggestions welcome here too! The problem that I'm about to describe, however, I also experienced when trying other methods based around floating and using two separate divs so the margin-right itself doesn't seem to be the root cause)
Now, I've managed to get the image to shrink and attach itself to the top of the page without too much trouble.
The problem that I'm having is with the margin-right. On page load, the <ul> appears centred and uniformly spread, as I want it to. The margin-right value on the third <li> is 0. However, the value jumps from 0 to 89px following the tiniest of scroll events. Continuing the scroll works as desired, but on scrolling back up, the margin returns to 89px and then stops.
Here's the code that I've got:
HTML:
<body>
<nav>
<ul>
<li>HOME</li>
<li>PROGRAMME</li>
<li id="grow_margin">TICKETS</li>
<li>MENU</li>
<li>VENUE</li>
<li>FAQ</li>
</ul>
</nav>
<header id="fix-it">
<h1><img id="scale-it" src="/images/logo.png" />TITLE</h1>
</header>
CSS:
html {
height: 100%;
width: 100%;
}
body {
height: 100%;
width: 100%;
}
nav {
position: fixed;
top: 0;
width: 100%;
height: 5%;
text-align: center;
}
nav ul {
list-style: none;
height: 100%;
}
nav ul li {
display: inline-block;
height: 100%;
}
nav ul li a {
color:#bbbbbb;
text-decoration: none;
margin: 8px;
}
header {
width: 100%;
position: fixed;
}
header h1 {
width: 100%;
text-align: center;
margin: 0;
padding: 0;
}
And the JS:
<script>
$(document).ready(function() {
var controller = $.superscrollorama({
reverse: true
});
var windowHeight=window.innerHeight;
var scrollDuration = windowHeight;
controller.addTween('#grow_margin',
TweenMax.to( $('#grow_margin'), .25, {css:{'margin-right':'250px'}, immediateRender:true}), scrollDuration);
controller.addTween('#scale-it',
TweenMax.fromTo( $('#scale-it'), .25, {css:{width:'650px'}, immediateRender:true, ease:Quad.easeInOut}, {css:{width:'250px'}, ease:Quad.easeInOut}),
scrollDuration);
controller.addTween('#fix-it',
TweenMax.fromTo( $('#fix-it'), .25, {css:{top:'40%'}, immediateRender:true, ease:Quad.easeInOut}, {css:{top:'4'}, ease:Quad.easeInOut}),
scrollDuration);
});
</script>
Any help would be so much appreciated! It's driving me absolutely crazy!
Thank you!
Rob

Categories