Sticky header animated linear background color - javascript

I´ve searched the web 100 of times to find something that is like that what I want. I found nothing and tried to do it myself. After two days I give up because of many reasons. So I´m asking you all here if some one can do it for me. Think about a sticks header, you scroll down a website and the header goes with it fixed on the top. So my imagine was, every time the header hits a section with data-color="#2D2D2D", the headers background color will change to it. But wait, I want that it happens linear with a background image, so if he scrolls back its the coloring linear to the color before.
Here is the article I´ve seen. But there its just a image and it is in the content.
https://codyhouse.co/demo/fixed-background-effect/index.html
Here is my Pen (It was just a try)
http://codepen.io/muuvmuuv/pen/MarxYx
Here is a img

I have made a basic example accordiong to your needs, just have a look and tell me if I understood what you said. I have added some extra explanation in the js code and the fiddle is at the end of this post.
A basic HTML markup
<heade id="webHeader">
<nav>
<ul>
<li>Nav item 1</li>
<li>Nav item 2</li>
<li>Nav item 3</li>
</ul>
</nav>
</heade>
<section id="section-1" data-color="#330000"></section>
<section id="section-2" data-color="#00B200"></section>
<section id="section-3" data-color="#803380"></section>
I am going for SCSS but you can easly update to basic CSS (I assumed that the sticky header is by default so I have added a padding to body with the same value as the header height)
$headerHeight: 100px;
body {
padding-top: $headerHeight;
}
#webHeader {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: $headerHeight;
background: #000F1F; /*default background color and fallback if there is no section available for it*/
nav {
padding: 40px;
float: right;
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #fff;
font-weight: 700;
text-decoration: none;
}
}
}
section {
width: 100%;
height: 500px;
background-color: grey;
border-bottom: 1px dashed #fff;
}
And the jQuery code.
(function($){
// cache dom elements
var $header = $('#webHeader');
var $window = $(window);
var headerHeight = $header.outerHeight(true);
var colors = []; // add colors here
var sections = []; // add sections positions
$('section').each(function(){
var $this = $(this);
colors.push($this.data('color'));
sections.push($this.position().top);
});
// duplicate first color
colors.unshift(colors[0]);
$window.on('scroll', function(){
var position = $window.scrollTop() + headerHeight;
var index = inInterval(position, sections);
var distance = position - sections[index];
$header.attr('style', linearGradient( colors[index+1], colors[index], distance ) );
}).trigger('scroll');
// trigger scroll when the page is loaded to update the header color to the current position
})(jQuery);
// Treat array elements as intervals
function inInterval(value, array) {
// cache array length
var arrLen = array.length;
// Add one more value at the end of array to avoid having problems on last item
array.push(array[arrLen-1]*2);
for (var i = 0; i < arrLen+1; i++)
if (value >= array[i] && value <= array[i+1])
return i;
}
function linearGradient(start, end, distance) {
var distanceStart = distance + '%';
var distanceEnd = 100 - distance + '%';
return "background: -webkit-gradient(linear, left top, left bottom, color-stop(0, "+ start +"), color-stop("+ distanceStart +", "+ start +"), color-stop("+ distanceStart +", "+ end +"), color-stop(100, "+ end +")";
}
You can see it working in this fiddle. I would make some updates but I am bit busy for moment, but I recommend you to read more about jQuery debounce and give a try to smart scroll (useful to call bit less scroll events - good for performance)
I hope is what you are looking for :)

Related

Move buttons up when scrolling to footer

I'm trying to push up two buttons vertically displayed in order to put them above the footer content when I'm scrolling to it.
My two buttons are like this:
And I want them to be like that:
Any ways to make it with Javascript?
Thanks a lot !
Since you provided no code at all, here's my solution based on the assumption your buttons are in a parent div and that div has position: fixed and a right / bottom -property set to a certain amount of pixel.
window.onscroll = function (ev) {
if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight - 2) {
document.getElementById("buttons").style.bottom = "100px";
} else {
document.getElementById("buttons").style.bottom = "20px";
}
}
/*
window.onscroll = function (ev) {
let footerHeight = document.getElementsByTagName("footer")[0].offsetHeight;
if ((window.innerHeight + window.pageYOffset) >= (document.body.offsetHeight - footerHeight)) {
document.getElementById("buttons").style.bottom = "100px";
} else {
document.getElementById("buttons").style.bottom = "20px";
}
}
*/
main {
height: 1000px;
}
#buttons {
position: fixed;
bottom: 20px;
right: 20px;
}
button {
background-color: grey;
border-radius: 50%;
display: block;
border: none;
padding: 15px;
margin-top: 10px;
}
<main></main>
<div id="buttons">
<button></button>
<button></button>
</div>
window.onscroll fires of at every scroll-event. It then checks, if the height of the window (window.innerHeight) and the distance scrolled so far (window.pageYOffset) are greater or equal to the total height of the body (document.body.offsetHeight)(- 2) is added because of an annoying mac-'feature'. For more look at this post). If thats the case, it moves the buttons up 100px instead of the 20px normally. If you dont add the else-statement, your buttons will stay at the position even if you scroll up again.
You can now get a bit creative. If you dont want to hit rock bottom of the page to make the buttons move, change the - 2. So you check for the height of your footer, and substract it from the total body height. Your buttons then start to move once the footer is it. Example of that in the javascript snippet, the part that is commented out.

jQuery: Add / Remove style while scrolling past div's

I have this page (https://www.datacoral.com/architecture/) that has a left sidebar with five bullet points. What I want to happen is as the user scrolls past these five div's on the right hand column, the text in one of these bullet points adds a class called 'bolder' and the text become a font-weight of 700 to let the user know what point they are in on the page. As the pass by that same div, the class disappears and ends up in the next bullet point since you're now passing by another div.
I've got it partially working but it's not hitting the right point of the div at all. Seems to add the class as you are passing the bottom of the div instead of the top.
This is the code I'm currently working with. Anyone know what I might be doing wrong so this can function properly?
Note: Should mention I'm basically duplicating this code five times and just swapping out the numbers.
jQuery(function(){
jQuery(window).scroll(function() {
var scroll = jQuery(window).scrollTop(); // how many pixels you've scrolled
var os = jQuery('#guide_body-0').offset().top; // pixels to the top of div1
var ht = jQuery('#guide_body-0').height(); // height of guide_body-0 in pixels
if(scroll > os + ht){
jQuery('.scroll-nav-0').addClass('bolder');
}
});
});
I think firing a function on scroll like that gets a little bit crazy. I always delay the function until the scrolling has stopped.
As far as the catch point, i think your code is applying the classes when the element has moved out of view. i would use the bottom of the browser screen as a reference point.
Think about it like this:
$(window).scrollTop() returns 0 at the top of the page.
$('#guide_body-0').offset().top returns 1000px.
So $(window).scrollTop() is equal to $('#guide_body-0').offset().top when that element is at the top of the screen.
Add $('#guide_body-0').height() to the equation and that puts the scroll position (the top of the screen) at the bottom of the element.
What you need to do is check if the offset.top property of the element is in a scroll position which puts it above the bottom of the screen.
UPDATE
The code here is for a custom solution. But if you are looking for a way to just add simple animations to elements as they scroll into view, check out wow.js and animate.css
https://wowjs.uk/
https://animate.style/
// Init variable for timer
let timer;
// Get target element
const el = $("#4");
// Get viewport height
const screen = window.innerHeight;
// Fire callback on window scroll
$(window).scroll(function() {
// Clear timeout just in case
clearTimeout(timer);
// Check if the element already has the class
if (!el.hasClass("active")) {
// Set a delay timer then run the function
timer = setTimeout(check_el_pos, 300);
}
});
function check_el_pos() {
// Clear the timer
clearTimeout(timer);
// Get current scroll position
let scroll_pos = $(window).scrollTop();
// This is the math here. Add scroll position to screen height and you get the bottom of the screen. When that value equals the top offset of the element, we are there.
if (scroll_pos + screen > el.offset().top) {
console.log(scroll_pos + screen);
console.log(el.offset().top);
// Add the classes to the element. Boom, we're done.
el.removeClass("active").addClass("active");
}
}
body,
html {
padding: 0;
margin: 0;
}
.example-grid {
list-style: none;
padding: 0;
margin: 0;
width: 100%;
display: grid;
grid-gap: 40px;
}
.example-grid>li {
list-style: none;
padding: 0;
margin: 0;
width: 100%;
height: 65vh;
background: slategray;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
color: #fff;
font-size: 2em;
line-height: 1em;
transition: background-color 1s;
transition-timing-function: ease-out;
}
.example-grid>li:nth-child(even) {
background: coral;
}
.example-grid>li.active {
background: aquamarine;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="example-grid">
<li id="1">1</li>
<li id="2">2</li>
<li id="3">3</li>
<li id="4">4</li>
<li id="5">5</li>
<li id="6">6</li>
<li id="7">7</li>
</ul>

how to set a scroll bar size without having content

I am trying to create a data-grid where I am not displaying all the records in the beginning and want to render them when scrolled. Is there a way I can set a scroll size based on the amount of data that I have and change the data in the dev when user scrolls.
I know there are grid's out there but I do not want to use them and want to know what are the different ways to do it.
I have tried looking into the scroll event and I did not find anything about the scroll direction or the current offset. Is there any documentation I can find. or even a technical name to search for would help me.
You can search for a "infinity scroll" with javascript and html.
EXAMPLE HTML: `<ul id='infinite-list'></ul>`
EXAMPLE CSS: `#infinite-list {
/* We need to limit the height and show a scrollbar */
width: 200px;
height: 300px;
overflow: auto;
/* Optional, only to check that it works with margin/padding */
margin: 30px;
padding: 20px;
border: 10px solid black;
}
/* Optional eye candy below: */
li {
padding: 10px;
list-style-type: none;
}
li:hover {
background: #ccc;
}`
EXAMPLE JAVASCRIPT: `var listElm = document.querySelector('#infinite-list');
// Add 20 items.
var nextItem = 1;
var loadMore = function() {
for (var i = 0; i < 20; i++) {
var item = document.createElement('li');
item.innerText = 'Item ' + nextItem++;
listElm.appendChild(item);
}
}
// Detect when scrolled to bottom.
listElm.addEventListener('scroll', function() {
if (listElm.scrollTop + listElm.clientHeight >= listElm.scrollHeight) {
loadMore();
}
});
// Initially load some items.
loadMore();
`

Change font color on scroll based on a div

I am currently coding a simple MENU button that is fixed in the top right of the screen.
With the text it is normally Black, but I want to be able to change the text to White when it is within a certain Div on a page so it is still visible on the dark background images.
I had set up two .CSS classes and tried to get them to switch on scroll but I cannot figure it out.
Anyone know how I can achieve this result?
HTML
<div class="NavigationButton menu_white">
MENU
</div>
CSS
.NavigationButton {
position: fixed;
top: 5%;
right: 5%;
z-index: 99999;
font-family: neuzeit-grotesk, sans-serif;
font-weight: 700;
color: inherit;
}
.menu_white {
color: #fff;
}
.menu_black {
color: #000;
}
(Not My Site) Example site: http://flavinsky.com/home/amaio
Just without the snap scroll
Thanks
You can use jQuery to get the scroll position and toggle the classes based on where the dark background element is. Here is an example
$(document).ready(function(){
$(window).scroll(function(){
var light_pos = $('#white_div').offset().top;
var light_height = $('#white_div').height();
var menu_pos = $('.NavigationButton').offset().top;
var scroll = $(window).scrollTop();
if(menu_pos > light_pos && menu_pos < (light_pos + light_height)) {
$('.NavigationButton').addClass('menu_black');
$('.NavigationButton').removeClass('menu_white');
}
else {
$('.NavigationButton').removeClass('menu_black');
$('.NavigationButton').addClass('menu_white');
}
})
})
and here is a working fiddle https://jsfiddle.net/atqkuwhs/
A possible solution is to get the offset of the div and the menu from the top of the page and apply your wanted changes once they intersect.

Animation performance jQuery, css transition ? How can i have a better performance here?

I want to implement a sort of a magic line that follow your mouse when you're on the header navigation menu.
For example :
http://www.infinitesquare.com/#actualites
Here it works perfectly, very smooth.
I typed a jQuery script myself, it works but it's not smooth as i want to.
Here is my HTML :
<nav>
<ul>
<li>
<p>Accueil</p>
</li>
<li>
<p>A propos</p>
</li>
<li>
<p>Contact</p>
</li>
<li>
<p>Nos technos</p>
</li>
<li id="magic-line"></li>
</ul>
</nav>
Here is the specific CSS:
#magic-line{
display: block;
position: absolute;
width: 100px;
height: 3px;
background-color: #7190C9;
left: 0px;
top:97px;
}
Here is my script :
var barre = $("#magic-line");
$("a").hover(function(){
var thisA = $(this);
switch(thisA.children("p").html()) {
case "Accueil":
var aWidth = thisA.parent("li").width();
var offset = thisA.offset().left;
barre.css({left : offset, width : aWidth, transition : "0.5s"});
break;
case "A propos":
var offset = thisA.offset().left;
var aWidth = thisA.parent("li").width();
barre.css({left : offset, width : aWidth, transition : "0.5s"});
break;
case "Contact":
var offset = thisA.offset().left;
var aWidth = thisA.parent("li").width();
barre.css({left : offset, width : aWidth, transition : "0.5s"});
break;
case "Nos technos":
var offset = thisA.offset().left;
var aWidth = thisA.parent("li").width();
barre.css({left : offset, width : aWidth, transition : "0.5s"});
break;
}
});
Do you have any idea how can i optimize this script ?
Here is a JsFiddle of my script :
https://jsfiddle.net/3jeu2L7v/
But it's smoother than on my website, how can i explain so ?
One way to improve animation performance is by using CSS transforms. So, instead of changing left and width we'd be using transform: translateX() scaleX().
The drawback is that CSS transforms are not supported by older browsers and you might have to deal with CSS prefixes in some cases (i.e. -webkit-transfor, -moz, etc), but CSS transforms will trigger hardware acceleration.
I suggest you try an animation library like GSAP that will take care of all this for you. It will use CSS transforms when available and add the required CSS prefixes if needed. Of course, this will add an extra dependency to your project and a few kilobytes ;)
var items = $('li');
var bar = $('.bar');
items.on('mouseover', function(){
var item = $(this);
var w = item.width();
var x = item.position().left + 15; // 15 is the margin form the CSS, change this accordingly
bar.css({
transform: 'translateX(' + (x + w / 2 - 1) + 'px) scaleX(' + w + ')',
//left: x,
//width: w
});
})
body {
font-family: sans-serif;
margin: 20px 50px;
}
nav{
padding: 15px 15px 20px 15px;
border: 1px solid #ccc;
position: relative;
}
nav ul {
list-style: none;
margin: 0;
padding: 0;
}
nav li {
list-style: none;
display: inline;
margin: 15px;
}
nav li a {
text-decoration: none;
color: #888;
}
nav .bar {
position: absolute;
width: 1px;
height: 3px;
left: 0;
background: #0ae;
bottom: 16px;
transition: 0.5s all;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav>
<ul>
<li>Home</li>
<li>About us</li>
<li>Work</li>
<li>Contact us</li>
</ul>
<div class="bar"></div>
</nav>
Here's a working example: http://codepen.io/victmo/pen/KMMNVG
If you get a change, check out this video / article about animation performance using CSS transforms. It's kind of old but still valid: http://www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/
Good luck!
Probably, the reason this animation runs slowly lies somewhere else in your codebase. But there's one thing I'd optimise in this code.
Namely, there's no need to disturb the DOM to calculate width and offset of the "Barre" each time user hovers. Much better to do it once (and update if layout changes) and use those precalculated values.
For example like this:
var barre = $("#barre-du-futur"),
// cache more selectors for later use
menuLink = $("a");
// This one will contain precalculated widths and offsets
var barreProps;
$(document).ready(function(){
barre.css({left : $(".active").offset().left, width : $(".active").parent("li").width(), transition : "0.5s"});
// Capture Barre properties
captureBarreProps();
});
$(window).resize(function(){
barre.css({left : $(".active").offset().left, width : $(".active").parent("li").width(), transition : "0.5s"});
// RECapture Barre properties
captureBarreProps();
});
// Optimization
function captureBarreProps() {
// Reset the object
barreProps = {};
// Loop through the links to precalculate properties
menuLink.each(function() {
var curMenuLink = $(this);
var curItemName = curMenuLink.children("p").html();
if ( curItemName.toString() !== "" ) {
barreProps[curItemName] = {
width: curMenuLink.parent("li").width(),
offset: curMenuLink.offset().left
};
} else {
// error: Something wrong with one of the Menu Items
}
});
}
// Now, there's no need in switch and hardcoding names
menuLink.hover(function(){
var thisA = $(this),
curItemName = thisA.children("p").html(),
curBarreProps = barreProps[curItemName];
barre.css({left : curBarreProps.offset, width : curBarreProps.width, transition : "0.5s"});
});
here's the fiddle
You need to trigger the hover on the li which is divided trough four (in your case) so that them fill the whole line from left to right without space between them, but getting the size of the a-Tag.
Impotrant is this line in your css
nav ul li {
width: 25%
}
And this is a quite shorter way to write your function:
$("li").each(function() {
$(this).hover(function (){
var trigger = $(this).find('a'),
left = trigger.offset().left,
width = trigger.outerWidth();
barre.css({'left': left, 'width': width})
})
})
I rewrote your fiddle:
Your rewritten fiddle

Categories