Does anyone know what javascript effects are being used to create the navbar effect on lesscss.org where the navbar only becomes fixed to the top after scrolling beyond a certain point. If anyone has actual code examples, or links to tutorials, that'd be appreciated.
it's a javascript check using the window.onscroll event
in the HTML source near the top:
window.onscroll = function () {
if (!docked && (menu.offsetTop - scrollTop() < 0)) {
menu.style.top = 0;
menu.style.position = 'fixed';
menu.className = 'docked';
docked = true;
} else if (docked && scrollTop() <= init) {
menu.style.position = 'absolute';
menu.style.top = init + 'px';
menu.className = menu.className.replace('docked', '');
docked = false;
}
};
Related
I have written some javaScript so that my menu starts off as position: static but will become position: fixed and stay at the top of the screen whenever the user scrolls upwards but will disappear again whenever scrolling downwards. Because the menu has some content above it, once the user has scrolled to the very top, the menu becomes position: static again.
This code works ok but I am having a problem when adding debounce. I've read I need either throttling or debounce for performance. I have tried using both the Lodash _.debounce and _.throttle functions separately. I don't mind having some delay on the menu showing itself on scroll-up, but with a debounce the header has a delay when returning to position: static once the user has scrolled back to the top of the page. I have tried using the {'leading': true} option for the debounce and throttle function but it hasn't done much good.
If I set my wait/delay time too low, surely there is no point in even using debounce or throttle anymore? I do not want to sacrifice the performance of the site but have been asked to implement this effect.
var header = document.getElementById("fixed-header");
var offset = header.offsetTop;
var $header = $(header);
var headerHeight = parseInt($header.css("height"));
var total = headerHeight + offset;
var lastScrollTop = 0;
window.addEventListener("scroll", _.debounce(scrollHeader, 200, {
'leading': true
}));
function scrollHeader() {
var st = window.pageYOffset || document.documentElement.scrollTop;
if (st > lastScrollTop) {
// downscroll code
if (pageYOffset >= total) {
document.body.classList.add("fixed");
document.body.classList.add("is-hidden");
document.body.style.paddingTop = header.offsetHeight + "px";
header.style.transition = ".5s";
} else {
document.body.classList.remove("fixed");
document.body.classList.remove("is-hidden");
document.body.style.paddingTop = 0;
}
} else {
// upscroll code
if (pageYOffset >= offset) {
document.body.classList.add("fixed");
document.body.classList.remove("is-hidden");
document.body.style.paddingTop = header.offsetHeight + "px";
} else {
header.style.transition = "initial";
document.body.classList.remove("fixed");
document.body.style.paddingTop = 0;
}
}
lastScrollTop = st;
}
I have a function which basically runs when an arrow with the class 'downArrow' is click. The function will find the parent of that arrow then find the next sibling with a class of 'scrollPoint' and then scroll to that area. Everything I just described works fine for me the issue I am having is if the bottom of the document hits the bottom of my viewport before the top of the element I am scrolling to hits the top of the viewport it just glitches out and scrolls back to the very top of the document. So I think What I need to do is detect if this scenario is going to happen and then set a max scroll value so the scroll functions doesnt try to scroll passed the bottom of the document.
How would I detect if the bottom of the document will be visible on the viewport and prevent from scrolling that far?
I will provide my code below in hopes that it will help, if you have any questions or need more clarification of what I am asking for just let me know. Thanks
This is my component although for what i am asking only the scrollTo function is really relevant
exports.init = init;
function init (options){
var downArrows = document.querySelectorAll(options.selector);
downArrows.forEach(triggerScrollHandler);
}
function scrollTo(element, to, duration) {
if (duration < 0) return;
var difference = to - element.scrollTop;
var perTick = difference / duration * 10;
setTimeout(function() {
element.scrollTop = element.scrollTop + perTick;
if (element.scrollTop === to) return;
scrollTo(element, to, duration - 10);
}, 10);
}
function scrollHandler (e) {
e.preventDefault();
var el = this,
scrollPoint = findSibling(el),
offsetVal = scrollPoint.getBoundingClientRect(),
windowOffset = window.pageYOffset;
offsetVal = offsetVal.top + windowOffset - 1;
scrollTo(document.body, offsetVal, 600);
}
function findParent(el) {
while (el && el.parentNode) {
el = el.parentNode;
if (el.tagName) {
return el;
}
}
return null;
}
function findSibling (el) {
var parent = findParent(el),
siblings = document.querySelectorAll('.scrollPoint'),
scrollTo;
siblings.forEach(function (currentSib, i) {
if(scrollTo == 'found'){
scrollTo = currentSib;
}
if(currentSib == parent){
scrollTo = 'found'
}
});
return scrollTo;
}
function triggerScrollHandler (el) {
el.addEventListener('click', scrollHandler);
}
And this is where I call in my app.js
var scrollHandler = require('./components/scrollHandler.js');
(function(){
scrollHandler.init({
selector: '.downArrow'
});
}())
Put this in your scroll listener:
if (document.body.scrollHeight <= document.body.scrollTop + document.body.clientHeight ){
console.log('scrolled to bottom');
}
Simple, pure JS solution :)
Good morning!
I want to share with you a simple script I made for the purposes of my company new website. It allows you to make a floating navigation bar which smoothly changes its background.
For now it's working with jQuery. My question is - is it possible to made this in pure CSS? My previous idea was to make navigation container with overflow: hidden and position: absolute + menu with position: fixed. Everything worked well until I realized that Firefox can't handle with this combination.
I'm waiting for yours ideas :)
Here's the code and preview:
var nav = $('.nav'),
navHeight = nav.height();
// Duplicate navigation
var navReversed = nav
.clone(true)
.addClass('nav-reversed')
.insertAfter(nav);
var navs = $('.nav'),
slides = $('.slide');
/* ... */
// onScroll event
$(window).on('scroll resize', function() {
var scrollTop = $(document).scrollTop(),
slide;
// Find first visible slide
slides.each(function() {
if ($(this).offset().top > scrollTop)
return false;
slide = $(this);
});
if (slide.length) {
var id = '#' + slide.attr('id'),
slideNext = slide.next('.slide');
var clipTop = clipBottom = 'auto';
if (slide.hasClass('slide-reversed')) {
clipBottom = Math.max(slideNext.offset().top - scrollTop, 0);
}
else {
clipTop = navHeight;
if (slideNext.length && slideNext.hasClass('slide-reversed')) {
clipTop = Math.min(slideNext.offset().top - scrollTop, clipTop);
}
}
if (clipTop !== 'auto') {
clipTop = Math.round(clipTop) + 'px';
}
if (clipBottom !== 'auto') {
clipBottom = Math.round(clipBottom) + 'px';
}
navReversed.css('clip', 'rect('+clipTop+',auto,'+clipBottom+',auto)');
/* ... */
}
}).trigger('scroll');
Full version: http://jsfiddle.net/greenek/NL7Fh/
You can try checkbox hack http://css-tricks.com/the-checkbox-hack/, there are :target too but you can't higlight the link. http://codepen.io/anon/pen/lqvpA
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 }
});
I added some special features to the sidebar of my webapplication. You can see a concept of the user interface on my testing site. (It's about the right sidebar)
The sidebar stops scrolling if it is scrolled to its end.
Moreover there are selected listitems in the sidebar wich stay on the top or the bottom of the sidebar if they would scroll out of the view.
My code is written in Javascript using jQuery. Unfortunately scrolling on my page is lagging now. Here are the links to my demo page (rightclick -> show sourcecode) and its javascript file.
How can I speed up the code (and let is still abstract) ?
I paste the javascript code here for those of you who don't want to follow the links.
HTML: (example)
<ul id="right">
<li><h3>Headline</h3></li>
<li><a>Item</a></li>
<li><a>Item</a></li>
<li><a class="selected">Active Item</a></li>
<li><a>Item</a></li>
<li><h3>Headline</h3></li>
<li><a>Item</a></li>
<li><a>Item</a></li>
</ul>
Javascript:
var Scrollers = $('#content,#left,#right');
var Scrollable = new Array(Scrollers.length);
var TopOffset = new Array(Scrollers.length);
var BottomOffset = new Array(Scrollers.length);
var OuterHeight = new Array(Scrollers.length);
var OuterHeightAndOffsets = new Array(Scrollers.length);
function ScrollInit(){
Scrollers.each(function(i){
// constants
TopOffset[i] = parseInt($(this).css("margin-top").replace("px",""));
BottomOffset[i] = parseInt($(this).css("margin-bottom").replace("px",""));
OuterHeight[i] = parseInt($(this).outerHeight());
OuterHeightAndOffsets[i] = TopOffset[i] + BottomOffset[i] + OuterHeight[i];
// classes
$(this).removeClass('snapped top bottom');
if(OuterHeightAndOffsets[i] < $(window).height()){
$(this).addClass('snapped top');
Scrollable[i] = false;
} else {
Scrollable[i] = true;
}
});
}
ScrollInit();
var SelectedListitems = $('li.selected');
var SelectedListitemsActive = new Array(SelectedListitems.length); for(var i=SelectedListitems.length; i<0; i++) SelectedListitemsActive[i] = false;
function ScrollCalc(){
// list item locking
SelectedListitems.each(function(i){
if(!($(this).parent().hasClass('snapped top'))){
var ListItemOffset = $(this).offset().top - $(window).scrollTop();
var ListItemState=0; // 0:in, 1:above, 2:under
if(ListItemOffset <= $(this).parent().offset().top){ ListItemState=1; }
else if(ListItemOffset + $(this).outerHeight() >= $(window).height()){ ListItemState=2; }
// no snapped clone so far
if(ListItemState){
if(SelectedListitemsActive[i]!=true && !$(this).parent().hasClass('snapped')){
var AppendClasses = 'clone snapped '; if(ListItemState == 1) AppendClasses += 'top '; else AppendClasses += 'bottom ';
$(this).parent().append($(this).clone().addClass(AppendClasses + i));
SelectedListitemsActive[i] = true;
}
// already snapped, clone existing
} else {
if(SelectedListitemsActive[i]==true){
$('.clone.snapped.' + i).remove();
SelectedListitemsActive[i] = false;
}
}
}
});
// scroll container locking
Scrollers.each(function(i){
if(Scrollable[i]){
if($(window).scrollTop()+$(window).height() > OuterHeightAndOffsets[i]){
$(this).addClass('snapped bottom');
} else {
$(this).removeClass('snapped bottom');
}
}
});
ScrollEvent = false;
}
ScrollCalc();
$(window).scroll(function(){
ScrollCalc();
});
I've just have a look at you link and believe that the lagging is not because of your javascript. If you don't think so try to disable all scripts in window.scroll event, still lagging right?
Now try to remove all shadow properties - box-shadow and text-shadow. Also remember to disable changing shadow opacity in simple.js (changing shadow during scroll event always laggy).
Now you can see it run very fast!!! Back to css file and enable each shadow properties and find out what is most suitable for you.
There is a much faster, easier way to get the effect you want.
Try this: when the window scrolls down far enough, set your sidebar's css position property to fixed. When it scrolls up, set the position of the sidebar back to relative.
var sidebar = document.getElementById('side'),
section;
sidebar.style.position = 'relative';
sidebar.style.bottom = '0px';
sidebar.style.right = '0px';
window.onscroll = function(){
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop,
maxTop = section ? section.offsetTop : sidebar.offsetHeight - window.innerHeight;
sidebar.style.top = sidebar.style.bottom = null;
if (scrollTop > maxTop) {
if (section) {
sidebar.style.top = - section.offsetTop + 'px';
} else {
sidebar.style.bottom = '0px';
}
sidebar.style.position = 'fixed';
} else {
sidebar.style.position = 'relative';
}
}
You can see it working here - http://jsfiddle.net/cL4Dy/