Change a header's position dynamically when activating drop down menu - javascript

I have a drop down menu that sits inside a header that is set to position: fixed. When viewed on a mobile device, I want the header to remain fixed, but when the menu is activated, jQuery dynamically changes the header's position to relative. That works fine (see code below), but there are a few problems that I need to fix. If the menu-toggle link is clicked again (closing the menu), the header does not return to its previous state "relative". Is there a way to do this? I also notice a flicker, so let's say you scroll half way down the page, then click on the menu to open it, the page sort of jumps and does not scroll back to the top where the menu is located inside the header as it should. I would prefer a pure CSS solution, but that seems impossible. I COULD set the rules so that if it's less than 499 pixels wide, the header gets positions "relative", but then usability fails, as a user will have to scroll up to the top of the page to access the drop down menu.
Any help would be greatly appreciated.
Here is my code:
HTML
<header role="banner" class="secondary">
<em>Menu</em> <span aria-hidden="true"></span>
<nav id="nav" role="navigation">
<ul class="menu set">
<li class="subnav">
Link
<ul class="sub-menu">
<li>Link</li>
<li>Link</li>
<li>Link</li>
</ul>
</li>
</ul>
</nav>
</header>
CSS
header[role="banner"] {
width: 100%;
background: #fff;
display: block;
margin: 0 auto;
padding: 0;
box-shadow: 0 2px 4px rgba(0,0,0,0.25);
z-index: 10000;
}
#media all and (min-width: 500px) {
header[role="banner"] {
position: fixed;
top: 0;
left: 0;
clear: both;
height: auto;
display: block;
}
}
#media all and (max-width: 499px) {
header[role="banner"] {
position: fixed;
}
}
JAVASCRIPT
$(document).ready(function() {
$('body').addClass('js');
var $menu = $('#nav'),
$menulink = $('.menu-toggle'),
$menuTrigger = $('.subnav > a');
$menulink.click(function(e) {
e.preventDefault();
$menulink.toggleClass('active');
$menu.toggleClass('active');
});
var add_toggle_links = function() {
if ($('.menu-toggle').is(":visible")){
if ($(".toggle-link").length > 0){
}
else{
$('.subnav > a').before('<span class="toggle-link">Open</span>');
$('.toggle-link').click(function(e) {
var $this = $(this);
$this.toggleClass('active').siblings('ul').toggleClass('active');
});
}
}
else{
$('.toggle-link').empty();
}
}
add_toggle_links();
$(window).bind("resize", add_toggle_links);
});
$(document).ready(function() {
$('.menu-toggle').click(function() {
if ($( document ).width() < 499)
$('header[role="banner"]').css('position', 'relative');
});
});

So you don't actually have any js code in there that will switch the position of your header between fixed and relative.
What I would do is maybe toggleClass on your header.
$(document).ready(function() {
$('.menu-toggle').click(function() {
if ($( document ).width() < 499){
$('header[role="banner"]').toggleClass('active');
}
});
});
Then in your css
header[role="banner"] {
position: fixed;
}
header[role="banner"].active {
position: relative;
}
The screen jump you're getting is probably from changing the position from fixed to relative, because relative will add the header back to the normal page flow. So to fix this, you could also toggle the class of whatever is below your header, so when active, it has a margin-top of 0, and when inactive, it has a margin-top equal to the height of the header.

Related

On button click show information sliding up and pushing the button upwards using jQuery

I have a foreach loop which displays a list of items using relative and absolute positioning, and on the bottom I would like to add a button (which is at the bottom of the container), which when pressed, shows/hides the given information, pushing the button with itself. I've looked at a couple of stackoverflow questions which had basically the same problem, but I couldn't find a solution which would work in my case.
Here are the codes for the problem (since I've tried a couple solutions, the style positions might not be logical, if you see anything weird please let me know):
The view:
<ul class="events>
#foreach (var events in Model)
{
//absolute positioned div-s
<li>
<div class="eventActions">
<button class="toggleBet">Place bet</button>
#Html.ActionLink("Event details", "Details", "Event", new { eventId = events.Id }, null)
<div class="betContent">#Html.Partial("_BetPartial", new BetViewModel(events))</div>
</div>
</li>
</ul>
The styles:
.events > li .eventActions {
position: absolute;
bottom: 0;
right: 0;
font-size: 24px;
height: 200px;
}
.events > li .toggleBet {
display: inline-block;
}
.events > li .betContent {
background-color: green;
margin: 0;
max-height: 0;
overflow: hidden;
transition: max-height 1s;
}
.events > li .eventActions.open .betContent {
max-height: 300px;
}
The jQuery:
$(".toggleBet").on("click",function(e) {
$(this.parentNode).toggleClass("open");
});
Here is a fiddle which shows what I would like to achieve: http://jsfiddle.net/yeyene/fpPJz/3/ (credits to user yeyene, from this question)
And here is the picture of my project so far (I would like to extend the list items height, move the links lower and make them move up when clicked)
Thank you in advance!
I would suggest forgetting about the .slideToggle method and just using a CSS class on the parent container, then use the max-height property to toggle between open and closed (or just height if you already know exactly how big the container should be when opened).
Here's a simple fiddle showing how you can do this with "pure" CSS by just adding a class to a container: https://jsfiddle.net/8ea3drce/
For good measure, below is the code used in the above JS fiddle:
HTML
<div class="container">
<a class="trigger">Trigger</a>
<ol class="content">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ol>
</div>
CSS
.container {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
}
.container .trigger {
display: inline-block;
background-color: red;
color: white;
cursor: pointer;
padding: 1em;
}
.container .content {
background-color: lightblue;
margin: 0;
max-height: 0; // This suppresses the element's height.
overflow: hidden; // This keeps internal elements from being visible when height is suppressed.
transition: max-height .5s; // This animates the motion when max-height is released. This isn't usually perfect. The closer max-height comes to be with the actual height of the element, the better. Fixed heights might be ideal.
}
.container.open .content {
max-height: 300px; // This releases the element's height to be as large as it would naturally be, up to 500px.
}
Javascript/jQuery
$('.trigger').on('click', function(e) {
$(this.parentNode).toggleClass('open');
})
Using the idea of classtoggling as shown in Dom's answer, setting the absolute position's anchors correctly and deleting the interfering height attribute solved the problem!

Bootstrap Scrollspy causes issues with Off-Canavas Menu

UPDATE: To clear up some confusion I added a fiddle that demonstrates how it is supposed to work, but it is just missing the
scrollspy: https://jsfiddle.net/kmdLg7t0/ How can I add the scrollspyto this fiddle so that the menu highlights when I'm on a specific section?
I created a fixed left menu that turns into an off-canvas menu at
<992px in browser width for tablet and mobile. When I select the anchor link on a browser width >992px it closes the menu and navigates to the anchor link section.
Custom JQuery Code:
This is my custom jQuery code that closes the Off-Canvas Menu when I click on an anchor link:
// close off-canvas menu and navigate to anchor
$('.navmenu-nav li a').on('click', function() {
$('body').removeClass('bs.offcanvas');
});
PROBLEM:
I decided to add a bootstrap offscrollspy and it works as intended after the browser width is greater than 992px, but when I resize the browser width to less than 992px this interferes with the Custom Jquery Code to close the menu and navigate to the anchor link.
Here's the Fiddle:
Bootstrap ScrollSpy causes issue with Off Canvas Menu and JQuery Code
My GUESS: I'm guessing the solution to this problem is to use jquery or
javascript to prevent or remove the data-target=".navmenu" from
activating when my screen is less than the <992px. Or we can find
a way to only activate the scrollspy after >992px. I'm
currently trying to figure this out, but I need someone who is a true
expert in jquery to solve this dilemma.
Pre-Requisite:
Bootstrap.min.css
Bootstrap.min.js
jasny-bootstrap.css
jasny-bootstrap.js
JS:
$(document).ready(function () {
toggleOffcanvas($(window).width() <= 992);
});
// simulate modal opening
$('.nav-link').click(function() {
if ($(window).width() > 992) {
$('.backdrop').hide(0, false);
}
$('#navToggle').click();
});
$('.navmenu').on('show.bs.offcanvas', function() {
if ($(window).width() <= 992) {
$('.backdrop').fadeIn();
}
});
$('.navmenu').on('hide.bs.offcanvas', function() {
if ($(window).width() <= 992) {
$('.backdrop').fadeOut();
}
});
// close modal on resize
$(window).resize(function() {
if ($(window).width() > 992) {
$('.backdrop').hide(0, false);
$('body').removeClass('bs.offcanvas');
}
toggleOffcanvas($(window).width() <= 992);
});
// switch active navigation link onclick
$('.nav a').on('click', function() {
$('.nav').find('.active').removeClass('active');
$(this).parent().addClass('active');
});
// close modal when navigating to anchor
$('.navmenu-nav li a').on('click', function() {
$('body').removeClass('bs.offcanvas');
});
function toggleOffcanvas(condition) {
if (!! condition) {
$('.nav-link').attr('data-toggle', 'offcanvas');
} else {
$('.nav-link').removeAttr('data-toggle');
}
}
html:
<body data-spy="scroll" data-target="#myScrollspy" data-offset="50">
<div class="backdrop"></div>
<div id="myScrollspy" class="navmenu navmenu-default navmenu-fixed-left offcanvas-sm colornav ">
×
<a id="navToggle" class=""><span></span></a>
<h4 class="navmenu-brand visible-md visible-lg visible-sm visible-xs" href="#">2017</h4>
<ul class="nav navmenu-nav">
<li class="active"><a class="nav-link" data-toggle="offcanvas" data-target=".navmenu" href="#january">Enero</a></li>
<li><a class="nav-link" data-toggle="offcanvas" data-target=".navmenu" href="#february">Msrs</a></li>
<li><a class="nav-link" href="http://www.jasny.net/bootstrap/examples/navmenu-reveal/">Jupiter</a></li>
<li><a class="nav-link" href="http://www.jasny.net/bootstrap/examples/navbar-offcanvas/">Off canvas navbar</a></li>
</ul>
</div>
<div class="navbar navbar-default navbar-fixed-top navbar-preheader">
<button type="button" class="navbar-toggle" data-toggle="offcanvas" data-target=".navmenu">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">navbar brand</a>
</div>
<div class="container">
<div class="page-header">
<h1>Navmenu Template</h1>
</div>
<p class="lead">This example shows the navmenu element. If the viewport is <b>less than 992px</b> the menu will be placed the off canvas and will be shown with a slide in effect.</p>
<p>Also take a look at the examples for a navmenu with push effect and reveal effect.</p>
<p class="space"></p>
<p id="january">January</p>
<p id="february">February</p>
</div><!-- /.container -->
</body>
CSS:
html, body {
height: 100%;
}
body {
padding: 50px 0 0 0;
}
.space {padding-bottom:900px;}
.backdrop {
background: rgba(0, 0, 0, 0.5);
position: fixed;
top: 0;
bottom: 0;
width: 100vw;
height: 100vh;
z-index: 1040;
display: none;
}
.navbar-fixed-top {
background:#fff!important;
}
.navbar {
display: block;
text-align: center;
}
.navbar-brand {
display: inline-block;
float: none;
}
.navbar-toggle {
position: absolute;
float: left;
margin-left: 15px;
}
.container {
max-width: 100%;
}
#media (min-width: 1px) {
.navbar-toggle {
display: block !important; background:none!important; border:none !important; color:#f90 !important;
}
}
#media (min-width: 992px) {
body {
padding: 30px 0 0 300px;
}
.navmenu {
padding-top: 0;
}
.navbar-toggle {display:none!important;}
.close {display:none}
.navmenu-fixed-left {
z-index:0;
top: 48px;
bottom: 0; background:#fff!important;
}
}
.navbar-default .navbar-toggle .icon-bar{
background-color:#333;
}
.close {margin-right:10px; margin-top:10px;}
#media (max-width:991px) {
.navmenu-fixed-left {
z-index:1050;
top: 0;
bottom: 0; background:#fff!important;
}
}
.backdrop {display:none}
#january, #february {
text-transform: uppercase;
background-color: red;
text-align: center;
line-height: 90vh;
font-size: 5em;
height: 90vh;
color: white;
}
#february {
background-color: green;
}
The problem with the code is that data-target=".navmenu" on menu items breaks the scrollspy plugin. Basically, scrollspy makes the connection between menu item and an element on the page via either data-target property or href property. Here is a part of it's source code:
return `${selector}[data-target="${target}"],` +
`${selector}[href="${target}"]`
Because of this you can't use data-target on menu links to close the menu. You can use javascript to close the menu instead.
Here is updated link's HTML:
<li class="active"><a class="nav-link" href="#january">Enero</a></li>
<li><a class="nav-link" href="#february">Msrs</a></li>
And all the javascript you need:
$(document).ready(function () {
// Add the backdrop when menu is shown
$('.navmenu').on('show.bs.offcanvas', function() {
$('.backdrop').fadeIn();
});
// Remove the backdrop when menu is hidden
$('.navmenu').on('hide.bs.offcanvas', function() {
$('.backdrop').fadeOut();
});
// Hide the menu on menu item click
$('.nav-link').click(function() {
if ($(window).width() <= 992) {
$('.navmenu').offcanvas('toggle');
}
});
// Remove the backdrop if window is resized over the breakpoint
$(window).resize(function () {
if ($(window).width() > 992) {
$('.backdrop').fadeOut();
}
});
});
A complete working example:
https://jsfiddle.net/kmdLg7t0/5/
Finally you have to remove all href="#" from link elements where they are not necessary. For example close menu button will take you back to # even if you have navigated to #january.
So things I did in total:
removed data- attributes from links
closing menu with javascript on link click
removed unnecessary href=# from links
Everything else is handled by plugins themselves.
By tying the scroll spy to a class, you can then toggle said class as needed. In addition, make sure to run the function once on page load to set initial state.
$('body').scrollspy({ target: '.scroll-spy' });
toggleScrollSpy($(window).width() <= 992);
// close modal on resize
$(window).resize(function() {
if ($(window).width() > 992) {
$('.backdrop').hide(0, false);
$('body').removeClass('bs.offcanvas');
}
toggleScrollSpy($(window).width() <= 992);
});
function toggleScrollSpy(condition) {
if (!!condition) {
$('#myScrollspy').addClass('scroll-spy');
} else {
$('#myScrollspy').removeClass('scroll-spy');
}
}
I would simply say when you animate or resize web page, make sure your coordinates top, left and height, width are carefully calculated. Because if they there is any change during resize it will show undesired positions. So its always good idea to examine coordinates and then alter dynamically them as the need arises.

Navigation bar is not responsive after scrolling

I am new to web development.I met a problem when designing a scrolling navigation bar.
I used a bootstrap grid and jquery for this. If I don't scroll down and just resize the window, then everything is fine. The navigation bar is resized and in the center of the window. But after I scroll down, the size of navigation bar will be fixed. Its size won't change when I make window larger(it will be smaller if I make window smaller, but not centered). This may caused by setting width: nav.width() in js file, but I don't know how to fix it.
This is a new account so I don't have enough reputation to upload images. I will try to describe it using words. For example, at beginning, the parent of navigation bar is 1140px, the width of navigation bar is 100%, so its size is 1140px. If I resize the window, let us say the width of its parent is 720px, then the navigation bar width is also 720px. Then if I scroll down, the callback function in js will run, it will set the width to 720px, which is a fixed value, thus it won't be responsible any more. Now, if I resize window to make its parent back to 1140px, the width of navigation bar is 720px rather than 1140. So how to fix this?
<div class="container">
<div class="row">
<div class="col-md-12">
<header id="home" style="height:300px">
<nav id="nav-wrap">
<ul id="nav" class="nav">
<li class="current">
Home
</li>
<li class="smotthscroll">
About
</li>
<li class="smotthscroll">
Portfolio
</li>
<li class="smotthscroll">
Skills
</li>
<li class="smotthscroll">
Contact
</li>
</ul>
</nav>
</header>
</div>
</div>
</div>
CSS
#nav-wrap {
font: 22px 'opensans-bold', sans-serif;
width: 100%;
margin: 0 auto;
left: 0;
top: 0;
}
ul#nav {
text-align: center;
padding: 0;
width: 100%;
background: rgba(0, 0, 0, 0.3);
z-index: 2;
}
ul#nav li {
height: 48px;
display: inline-block;
}
ul#nav li a {
color:white;
padding: 8px 13px;
display: inline-block;
text-align: center;
}
$(function() {
var nav = $('#nav');
var navHomeY = nav.offset().top;
var isFixed = false;
var navWrap = $('#nav-wrap');
var $w = $(window);
$w.scroll(function () {
var scrollTop = $w.scrollTop();
var shouldBeFixed = scrollTop > navHomeY;
if(shouldBeFixed && !isFixed) {
nav.css({
position: 'fixed',
width: nav.width()
});
isFixed = true;
}else if (!shouldBeFixed && isFixed) {
nav.css({
position: 'static'
});
isFixed = false;
}
});
});
Your problem most likely comes from this part.
var shouldBeFixed = scrollTop > navHomeY;
if(shouldBeFixed && !isFixed) {
nav.css({
position: 'fixed',
width: nav.width()
});
isFixed = true;
Try inversing or removing that part all together. It is saying if the navbar is not at the top, add a position of fixed to it as well as nav.width(), whatever you set that to equal - and this is precisely the opposite of what you want because it's what your problem is according to the question.

Javascript - Sticky button depending on the width header

I'm trying to make a sticky header with an ul and one div.
The sticky header work fine, but i want to make sticky the div too, only when the screen width are >= 981px.
Code
HTML:
<div id="sub-header-info">
<div class="wrap-perfil">
<nav class="barra-header">
<ul>
<li><a class="actividad" href="index.php">Actividad</a></li>
<li><a class="favoritos" href="#">Favoritos</a></li>
<li><a class="seguidores" href="seguidores.php">Seguidores</a></li>
<li><a class="seguidos" href="seguidos.php">Siguiendo</a></li>
</ul>
</nav>
</div>
</div>
<div class="wrap-perfil">
<div class="boton-seguir">Seguir</div>
</div>
CSS:
.boton-seguir {
float:right;
position: relative;
top: -40px;
}
.boton-fixed {
position: fixed;
top: 161;
right: 90px;
}
JavaScript:
var stickyOffset = $('#sub-header-info').offset().top;
$(window).scroll(function(){
var sticky = $('#sub-header-info'),
scroll = $(window).scrollTop();
if (scroll >= stickyOffset) sticky.addClass('fixed');
else sticky.removeClass('fixed');
});
var stickyOffset2 = $('.boton-seguir').offset().top;
var boton = $('.boton-seguir');
scroll = $(window).scrollTop();
if (screen.width >= 981 && scroll >= stickyOffset2)
boton.addClass('boton-fixed')
What I spected:
Here is a Live Demo .
What is wrong here? Can anyone help me?
Thanks in advance.
First of all, because your button is not inside of the sticky div, you're going to have to make it sticky as well. Modify your JavaScript like this:
var stickyOffset = $('#sub-header-info').offset().top;
$(window).scroll(function(){
var sticky = $('#sub-header-info'),
scroll = $(window).scrollTop(),
stickyBtn = sticky.next();
if (scroll >= stickyOffset) {
sticky.addClass('fixed');
stickyBtn.addClass('fixedBtn');
}
else {
sticky.removeClass('fixed');
stickyBtn.removeClass('fixedBtn');
}
});
Now you can catch that button with media queries like this:
#media only screen and (min-width: 981px) {
.wrap-perfil.fixedBtn {
position:fixed;
/* We need a z-index greater than #sub-header-info's */
z-index: 1000000;
/* Probably better to remove the negative-top off of the child element,
* but this hack will demonstrate how it works. :) */
top: 60px;
}
}
#media only screen and (max-width: 980px) {
.wrap-perfil.fixedBtn {
/* Add CSS for smaller screens here. */
}
}

Side menu in JS

I'm trying to make a side menu but it's not perfect. Take a look:
JSFiddle
HTML
<div id="content-button">
<div id="open-menu">Click</div>
</div>
<div id="content">
<nav id="side-menu">
<ul>
<li>Link</li>
<li>Link</li>
<li>Link</li>
</ul>
</nav>
</div>
Javascript
$(function() {
$("#open-menu").click(function() {
var $cache = $("#side-menu");
if($cache.is(":visible"))
{
$cache.hide("slide", {direction:"left"}, 250);
}
else
{
$cache.show("slide", {direction:"left"}, 250);
}
});
});
CSS
#content-button {
width: 100%;
height: 100px;
}
#open-menu {
width: 50px;
height: 50px;
}
#side-menu {
position: relative;
left: 0;
}
When I click on "click", the side menu appear on the left of my screen BUT :
it appears suddenly, not with slide effect
it appears at the top of div "content" and push all the content. I want it to appear at the left and push the entire content to the right
it doesn't disappear when I click a second time on "click"
I hope you understood my problem. I can't show you on JsFiddle, it doesn't work and I'm on local but I can do some screenshot if needed.
"slide" isn't a valid parameter to show/hide.
Instead, you can use animations.
$(function() {
$("#open-menu").click(function() {
var $cache = $("#side-menu");
if($cache.position().left === 0)
{
$cache.animate({
left: -300
}).then(function(){
$cache.hide();
});
}
else
{
$cache.show();
$cache.animate({
left: 0
});
}
});
});
Make sure your menu is positioned relatively.
#side-menu {
position: relative;
/* left: -300px */ /* uncomment to default hidden */
}
demo

Categories

Resources