StickyNav - debug - javascript

// STICKY NAVBAR
var num = 816; //number of pixels before modifying styles
$(window).bind('scroll', function () {
if ($(window).scrollTop() > num) {
$('.navbar').addClass('navbar-fixed-top');
} else {
$('.navbar').removeClass('navbar-fixed-top');
}
});
Thisis my code for the sticky navbar (using a custom Bootstrap navbar), and It works fantastic on Desktop browsers...but the var num is measured in pixels instead of em. That doesn't translate well to mobile.
Is there a way to:
Measure the scroll in em instead of px
or...
Detect when the bar hits the top of the window and then make it stick instead of using hard-coded measurements?

The problem you're having is not related to a different measurement on mobile devices, cause it's always measured and calculated in pixels on any device. It's more likely that the position of your navbar is just different on a mobile device.
And yes, a solution would be to get the current offset of your navbar and use that instead of the hard coded 'num', like so:
var navbarOffset = $('.navbar').offset();
var num = navbarOffset.top;
But please keep in mind that this approach assumes that the position of the navbar will not change during the its lifetime. Once your dealing with a responsive design you should recalculate the navbar offset every time the size of a content wrapper element changes.
How elaborate this whole thing can get you can check out here:
https://github.com/zurb/foundation/blob/master/js/foundation/foundation.topbar.js

Related

How to use Javascript to force "div.classname" to full screen width?

I would like to use some basic Javascript to automatically set the width of a div element (class name = "tb-megamenu-submenu") to be the full width of the screen and centered. I would also like this calculation to run any time the screen is resized.
Normally I would just use CSS for this (width: 100vw), but the parent element is position:relative and the submenu is position:absolute, so any attempt to set the width fails because the submenu cannot be centered on the screen with CSS alone.
I'm using a Drupal Module called "The Better Mega Menu." There is a working example of a websites that does this exact thing that I want (https://www.hollyhunt.com/), but I can't seem to replicate their success. Here's the code they are using on their site:
// Make submenu full browser width.
const submenuFullwidthCalc = function () {
// Get the Mega menu Level 1 sub menu.
$(".tb-megamenu-nav > .level-1 > .tb-megamenu-submenu").each(function () {
// reset to zero so it can be calculated again and again
$(this).css("left", 0);
const offsettarget = $("body").offset();
// The offset of this submenu.
const offsetthis = $(this)
.parent()
.offset();
// Calculate the offset.
$(this).css("left", offsettarget.left - offsetthis.left);
// Set the submenu full width.
$(this).css("width", $("body").width());
});
};
How can I get this kind of functionality working on my site? Oh, and I'm stuck using the old BootStrap 3 Theme, so any solutions may have to be compatible with older code standards. Thanks for any help you can give!!!

On scroll animation issue on small screens using Vanilla JS

I’ve created an animation for my website to change a certain element (for example its background colour) while scrolling using Vanilla JS. For this I have used the window.onscroll property and I trigger the animation when window.scrollY reaches a specific position, my code is:
window.addEventListener('scroll', () => {
if (window.scrollX >= 1000) {
box.style = "background:red"
}
})
It looks great when I am editing on my big screen resolution, but if I look at the page on my laptop, the animation gets messed up because the innerWidth and innerHeight of the screen are different. I want to trigger the animation dynamically if it reaches a certain section of the page without having to worry about the scroll position.
Does anyone have any ideas about how I can fix this?
I think using getBoundingClientRect() solves your problem.
You basically do something like this:
var my_box = document.getElementById('my-box');
var rect = my_box.getBoundingClientRect();
The rect variable now contains an Object which includes a top and a left property with values that are relative to the viewport.
https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect

how to determine finished scrolling position of element in responsive parallax site with javascript

I have a parallax web page with a series of modules that contain a large photo and a smaller text box that overlays the photo and sits absolutely positioned at the bottom of it. While the size of the photos change, the text box is consistently 340px. Initially, when the site scrolls, the text box is hidden (I am doing a translateY(340px) and hiding the overflow on the container).
I know how to determine when to start revealing the box:
window.addEventListener('scroll', self.monitorScroll, false);
var moduleOffset = Math.floor($el.offset().top);
var moduleTriggerPos = moduleOffset - self.windowHalfHeight; //trigger animation when module is halfway up the screen
self.monitorScroll = function(){
self.yPos = window.pageYOffset;
if (self.yPos > thisObject.triggerPos){
//BEGIN ANIMATION
}
}
but I don't know how to tell the object how much to move each time the listener is triggered. The number of times it is called seems to be based on how fast you scroll, and the amount I would need to move the object differs, as the container resizes with the browser (the narrower the browser, the smaller the photo becomes).
I am currently using a static amount to move the object:
if (newPos >= 0){ //if our object hasn't reached a translateY(0px) value
thisObject.curPos = 320 //starts at 320, the amount it has been translated by in order to hide it
var newPos = thisObject.curPos - 25; //the amount we move it each time
thisObject.textEl.css({ translate: [0,newPos] }); //move it by 25px
thisObject.curPos = newPos; //update the current position
}
How do I get more accurate determination of how much to move the item by, rather than using a static movement amount. I want it to be based on the percentage of the way I've scrolled toward the module's final reveal position, which would ideally be when the module was fully at the top of the screen at full browser width (max width of 1200px), or some percentage thereof if they had resized the browser smaller.
I don't need exact code, but more just a conceptual understanding of what I should be monitoring / calculating to determine the correct positioning. Thanks!
I figured this out. For various reasons I made the trigger point for the CSS animation start when it first appears on screen rather than halfway up, then used this code:
var scrollProgress = (((self.windowHeight+self.yPos)-thisObject.offset)/thisObject.height);
var scrollAmt = scrollProgress*self.moduleTextHeight;
var translateAmt = -scrollAmt;
if (scrollAmt <= self.moduleTextHeight){
thisObject.textEl.css({ translate: [0,translateAmt] });
} else {
thisObject.textEl.css({ translate: [0,-self.moduleTextHeight] });
}

Calculating Height of Sidebar Dynamically

I'm trying to work out the algorithm for a fixed div that grows in height (while scrolling) until it's equal to the height of the viewport or div with fixed position relative to another div and the bottom of the viewport
I am using Twitter Bootstrap affix to lock my secondary navigation bar (yellow) and my sidebar (black) to the top of the screen when the user scrolls that far. 
This works fine. The sidebar is the piece that's giving me trouble.  When it is in its in its starting position (as shown in the diagram belorw), I want the top of the bar to sit 30px
down from the secondary navigation bar (yellow) and 30px up from the bottom of the page. 
As the user scrolls, the bar should grow in height so that it remains 30px beneath the secondary navigation bar and 30px above the bottom of the screen (As shown in the diagram below)
After the bar is fixed position, I am able to do what I need to do.  
.sidebar { 
position:fixed;
top:100px;  
bottom:30px;
left:30px;
}
What I can't figure out is how to position the TOP of the sidebar relative to my
secondary navigation bar and the BOTTOM of my sidebar relative to the bottom
of the screen. I've tried calculating the height of the sidebar at the beginning and the end of the
scroll but this causes issues.
I've also tried calculating the final height of the sidebar and letting the bottom of
the sidebar just run off the edge of the screen (when it's in its initial position), but
if there's not enough content on the right side to warrant scrolling, I have no way
of getting to the bottom items in the scroll bar.  Plus my screen starts bouncing
in a really un­attractive way.
below is the current code in use:
ShelvesSideBar.prototype._resize_sidebar = function(_this) {
var PADDING = 50;
var window_height = $(window).height(),
nav_bar_height = $('.nav_bar').height() + $('.secondary_tabs').height(),
sidebar_height = window_height - nav_bar_height - PADDING,
sidebar_scrollable_height = sidebar_height - $('.bar_top').height();
_this.$container.height(sidebar_height);
_this.$container.find('.bar_bottom').height(sidebar_scrollable_height);
/* reset the nanoscroller */
_this.$container.nanoScroller();
};
   
this code is called on page load and again on window resize. Any help would be greatly appreciated!
I've been trying to do something similar (minus the fixed elements and navbars). What I found was in order to do any sort of relative height scaling every element above the element I wished to scale all the way up to the opening html tags had to have a relative height set, even if it was just height:100%;. (here's my original question Variable height, scrollable div, contents floating)
My goal was to have the body height fixed to window size like a native full screen application would be with my content subareas scrolling, so this is a bit off topic for what you're wanting to accomplish. But I tried using JS/JQ to start off with as you're trying to do currently and found that I simply couldn't get the window height because the default behaviour for height management is to expand the page height until everything on the page fits. And all the getHeight methods I tried we're getting the page height not window/viewport height as promised. So you may wish to try fixing your body's height to the window and going from there using overflow:scroll; to scroll the page.
A quick note on overflow:scroll; if you have users who use WP8 IE (and probably other versions of IE) it may be advantageous to implement FTscroller to handle all your scroll elements as the overflow property defaults to hidden and is a fixed browser property. The only problem with FTscroller is because it uses CSS offsets to move the content container it may wreak havoc on elements that are designed to switched to fix when they reach x height from top of page because technically the top of page (or rather the top of the container they're in) isn't at the top of the page anymore it's beyond it. Just something to be aware of if you do need to cater for this browser.
And apologies for the complexity of my sentence structure. :/
so I was able to figure this out, for anyone still looking. What I ended up doing was binding to the window scroll event and - whenever the scroll occurred - I check if the class "affix" has been added to the sidebar. If it has, then I perform one set of calculations to determine sidebar height. Otherwise, I perform the other set of calculations. Code below:
/* called on window scroll */
var PADDING = 70;
var window_height = $(window).height(),
nav_bar_height = $('.nav_bar').height() + $('.secondary_tabs').height(),
header_height = $('.prof_block').height() - nav_bar_height,
sidebar_height = _this.$container.hasClass("affix") ? window_height - nav_bar_height - PADDING : window_height - (header_height + nav_bar_height) - PADDING,
sidebar_scrollable_height = sidebar_height - $('.bar_top').height();
_this.$container.height(sidebar_height);
_this.$container.find('.bar_bottom').height(sidebar_scrollable_height);

window scroll method flickers in IE

This may come as a huge surprise to some people but I am having an issue with the IE browser when I am using the $(window).scroll method.
My goal:
I would like to have the menu located on the left retain it's position until the scroll reaches > y value. It will then fix itself to the top of the page until the scroll returns to a < y value.
My error:
Everything seems just fine in Chrome and Firefox but when I go to Internet Explorer it would seem the browser is moving #scroller every time the scroll value changes, this is causing a moving/flickering event.
If someone could point me to a resource or give me a workaround for this I would be very grateful!
Here is a fiddle:
http://jsfiddle.net/CampbeII/nLK7j/
Here is a link to the site in dev:
http://squ4reone.com/domains/ottawakaraoke/Squ4reone/responsive/index.php
My script:
$(window).scroll(function () {
var navigation = $(window).scrollTop();
if (navigation > 400) {
$('#scroller').css('top',navigation - 220);
} else {
$('#scroller').css('top',183);
$('#scroller').css('position','relative');
}
});
You might want to take a look at the jQuery Waypoints plugin, it lets you do sticky elements like this and a lot more.
If you want to stick with your current method, like the other answers have indicated you should toggle fixed positioning instead of updating the .top attribute in every scroll event. However, I would also introduce a flag to track whether or not it is currently stuck, this way you are only updating the position and top attributes when it actually make the transition instead of every scroll event. Interacting with the DOM is computationally expensive, this will take a lot of load off of the layout engine and should make things even smoother.
http://jsfiddle.net/WYNcj/6/
$(function () {
var stuck = false,
stickAt = $('#scroller').offset().top;
$(window).scroll(function () {
var scrollTop = $(window).scrollTop();
if (!stuck && scrollTop > stickAt) {
$('#scroller').css('top', 0);
$('#scroller').css('position','fixed');
stuck = true;
} else if (stuck && scrollTop < stickAt) {
$('#scroller').css('top', stickAt);
$('#scroller').css('position','absolute');
stuck = false;
}
});
});
Update
Switching the #scroller from relative to fixed removes it from the normal flow of the page, this can have unintended consequences for the layout as it re-flows without the missing block. If you change #scroller to use an absolute position it will be removed from the normal flow and will no longer cause these side-effects. I've updated the above example and the linked jsfiddle to reflect the changes to the JS/CSS.
I also changed the way that stickAt is calculated as well, it uses .offset() to find the exact position of the top of #scoller instead of relying on the CSS top value.
Instead of setting the top distance at each scroll event, please consider only switching between a fixed position and an absolute or relative position.All browsers will appreciate and Especially IE.
So you still listen to scroll but you now keep a state flag out of the scroll handler and simply evaluate if it has to switch between display types.
That is so much more optimized and IE likes it.
I can get flickers in Chrome as well if I scroll very quickly. Instead of updating the top position on scroll, instead used the fixed position for your element once the page has scrolled below the threshold. Take a look at the updated fiddle: http://jsfiddle.net/nLK7j/2/

Categories

Resources