Bootstrap Scrollspy causes issues with Off-Canavas Menu - javascript

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.

Related

I neeHelp at making a responsive menu

So i want to make this responsive menu. On Desktop it looks like this:
And on Mobile it should look stacked overlapping everything under it but not pushing it down. So not like this:
(Before button pressed)
(After button pressed)
You can see that the Slideshow below is pushed down and the obvious misplacement of the menu on the button in general.
Plese help me to fix this, im a poor backend dev.
Here is my code:
function myFunction() {
var x = document.getElementById("menu");
if (x.className === "menu") {
x.className += " responsive";
} else {
x.className = "menu";
}
}
.menu .icon {
display: none;
}
#media screen and (max-width: 965px) {
.menu a {display: none;}
.menu a.icon {
float: right;
display: block;
}
}
/* The "responsive" class is added to the menu with JavaScript when the user clicks on the icon */
#media screen and (max-width: 965px) {
.menu.responsive {
background-color: white;
position: relative;
}
.menu.responsive a.icon {
position: absolute;
right: 0;
top: 0;
}
.menu.responsive a {
float: none;
display: block;
text-align: left
}
}
<div class="mainheader">
<div class="logo">
<img src="../bilder/Logo_Koeln_Fliesen_Esch.jpg">
</div>
<div id="menu" class="menu">
Unternehmen
Leistungen
Referenzen
Kontakt
<a href="javascript:void(0);" class="icon" onclick="myFunction()">
<img class="bigicon" src="../bilder/menu.png">
</a>
</div>
</div>
So the anwer to fix the issue was to set the responsive menu class to absolute, also you have to use right: 0; so it stays in place.
After that i figured out that i could just move the menu links down since they are also absolute now in order to prevent them from overlapping the button.
Thanks for the effort of helping everyone ;)
function myFunction() {
var x = document.getElementById("menu");
if (x.className === "menu") {
x.className += " responsive";
} else {
x.className = "menu";
}
}
.menu .icon {
display: none;
}
#media screen and (max-width: 965px) {
.menu a {display: none;}
.menu a.icon {
float: right;
display: block;
}
}
/* The "responsive" class is added to the menu with JavaScript when the user clicks on the icon */
#media screen and (max-width: 965px) {
.menu.responsive {
background-color: white;
position: relative;
}
.menu.responsive a {
float: none;
display: block;
text-align: right
}
}
<img src="../bilder/Logo_Koeln_Fliesen_Esch.jpg">
</div>
<div id="menu" class="menu">
<img class="bigicon" src="../bilder/menu.png">
<span>
Unternehmen
Leistungen
Referenzen
Kontakt
</span>
</div>
</div>
with out looking at whole page code here is what you need to do or something I would always check before going forward.
for css to work properly add this meta tag in head <meta name="viewport" content="minimal-ui, height=device-height, width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
secondly menu is something that should be stacked higher up on the interactive and display layer so it does not mess with other elements on the page. One way of doing this is to place the tag at the bottom of everything last on page (remember to css absolute positioning on the menu div) or zoom it up like +10000 should do.
please try not to add responsiveness after the fact like when user clicks this will create whole lot of problem going forward as computed vs initial values collide. Leave things to css as much as possible. It is good code from w3school follow its instructions as stated.
For the positioning issue calculate height of the image;
it is actually your icon that is being positioned every thing else is relative to where it should be. so move your icon as first item then every other anchor tag below it. this should solve your positioning issue.
Add z-index property to .menu.responsive class.

Disable and Enable Scroll Window when resizing viewports in jquery

I have here a navbar with a dropdown menu and I wanted the page to stop scrolling when I stretch out the whole screen to another resolution. I have reviewed my code and I think the problem is the conditions in my if-else statement. I am stuck in this for hours now and I cannot formulate a solution to my problem. I also added a label in my html aria-label="hidden" to use for enabling and disabling the whole page scroll. Can someone enlighten me with this problem I am currently having now?
Here is my code snippet:
<div class="c-dropdown-btn"><i class="fas fa-bars c-grey-pill"></i></div>
<div id="c-nav-mobile" aria-label="hidden">
<ul class="c-nav-links2">
<li>カテゴリ1</li>
<li>カテゴリ2</li>
<li>カテゴリ3</li>
<li>カテゴリ4</li>
</ul>
<div class="c-img-cont2">
<div class="c-image-res3"></div>
<div class="c-consult-cont2">
<div class="c-con-rel"><span class="c-consult-text2">自分のキャリアに合った留学を選ぶための無料相談会実施中</span>
<button class="c-btn c-btn-primary consult-text">詳しくはこちら→</button>
</div>
</div>
</div>
</div>
And here is my jquery code for this:
$('.c-dropdown-btn i').click(function () {
$(this).toggleClass('fa-times');
$('#c-nav-mobile').fadeToggle("on");
bodyStyle();
});
$(window).resize(function(){
bodyStyle();
});
function bodyStyle() {
var viewportWidth = $(window).width();
var navMobile = $("#c-nav-mobile");
if(viewportWidth < 768 && navMobile.attr("aria-label") == "hidden") {
$("body").css("overflow", "hidden");
console.log("abc");
} else if (viewportWidth > 769 && navMobile.attr("aria-label") == "hidden"){
$("body").css("overflow", "none");
console.log("123");
}
}
for scss snippet (for #c-nav-mobile)
#c-{
&nav-mobile {
display: none;
position: absolute;
left: 0;
z-index: 1;
top: 41px;
width: 100%;
height: 523px;
padding: 25px 32px 34px 31px;
background: rgba(0,0,0,0.5);
#include mq('md') {
display: none !important;
}
&.on {
display: block;
}
}
}
I declared a function and called it inside resize and also to my click event.
#c-nav-mobile add this:
box-sizing: border-box;
CSS media queries do most of the work for you.
You can eliminate much of the JS here by using css rules inside a class. Then only use JS to toggle the tag - which you are already doing but do so on the body tag.
HTML (just added the body tag for completeness)
<body>
<div class="c-dropdown-btn"><i class="fas fa-bars c-grey-pill"></i></div>
<div id="c-nav-mobile" aria-label="hidden">
<ul class="c-nav-links2">
<li>カテゴリ1</li>
<li>カテゴリ2</li>
<li>カテゴリ3</li>
<li>カテゴリ4</li>
</ul>
<div class="c-img-cont2">
<div class="c-image-res3"></div>
<div class="c-consult-cont2">
<div class="c-con-rel"><span class="c-consult-text2">自分のキャリアに合った留学を選ぶための無料相談会実施中</span>
<button class="c-btn c-btn-primary consult-text">詳しくはこちら→</button>
</div>
</div>
</div>
</div>
</div>
</body>
CSS
// code below works if it's the body that is doing the scrolling
// if not, use the scrolling element instead here and the JS click
#media only screen and (max-width: 768px) {
body.fa-times {
overflow: hidden;
// might need to set a height
height: 100vh;
}
}
JS
$('.c-dropdown-btn i').click(function () {
$('body').toggleClass('fa-times');
$('#c-nav-mobile').fadeToggle("on");
});

Why does mobile nav button expand on page load?

I have a mobile nav button that upon touching/clicking, should expand and reveal page links. Problem is when you first start the page the button is already expanded:
But should actually load page with elements hidden like so:
The X icon and Line-stack Icon are also reversed. How would I switch these icons around and also make sure the page loads with them closed? I tried switching the icons classes in the jQuery function to switch the x and line-stack but that hasn't worked.
I know there is a simple concept I am missing but I am quite new to jQuery and am having trouble here.
My HTML:
<nav>
<div class="row">
<img src="img/logoblack.png" alt="logo" class="logo img-fluid">
<img src="img/logoblack.png" alt="logo" class="logo-black">
<ul class="main-nav js--main-nav">
<li>About</li>
<li>Skill</li>
<li>Résumé</li>
<li>Contact</li>
</ul>
<a class="mobile-nav-icon js--nav-icon"><i class="ion-navicon-round"></i></a>
</div>
</nav>
My CSS:
.mobile-nav-icon {
float: right;
margin-top: 30px;
cursor: pointer; /* Used since no href tag specifying link type */
display: none;
}
My jQuery:
$('.js--nav-icon').click(function() {
var nav = $('.js--main-nav');
var icon = $('.js--nav-icon i');
nav.slideToggle(200);
if (icon.hasClass('ion-navicon-round')) {
icon.addClass('ion-close-round');
icon.removeClass('ion-navicon-round');
} else {
icon.addClass('ion-navicon-round');
icon.removeClass('ion-close-round');
}
});
Figured it out, needed to create a media query that would hide the .main-nav with display-none;. This way the tags were hidden on mobile devices but still shows in a navbar on a browser:
/* Small phones to small tablets from: 481px to 767px*/
#media only screen and (max-width: 767px) {
.main-nav {
float: left;
margin-top: 25px;
margin-left: 25px;
display: none;
}
}

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

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.

jQuery Toggle does not show on window resize

I'm using jQuery slideToggle function and the media queries.
The problem is that when I resize the window to small size, the toggle links appear. Now if I click on toggle, it works fine, but when I resize the window back to large size, the hidden element still do not appear.
If I do not click on the toggle link, and resize the window back to large size, it works fine.
Demo to see the problem:
Please check the demo here, where you can see the problem:
http://jsfiddle.net/3Jj7J/
Resize the window to small size that you see "Main Menu" link. When you click on it, you will see the toggle. Now if you resize it back to large size, the normal links will still not appear.
Here's my code:
HTML:
<div class="bar">
<a class="toggle" href="#">MAIN MENU</a>
</div>
<div class="nav">
<div class="wrap">
<ul>
<li>Link 1</li>
<li>Link 2</li>
</ul>
</div>
</div>
CSS:
.bar{
display: none;
border: 1px solid red;
}
#media screen and (max-width: 500px) {
.nav ul {
display: none;
}
.bar{
display: block;
}
}
jQuery:
var menu = jQuery('.nav .wrap > ul');
jQuery(".toggle").click(function() {
menu.slideToggle(500);
});
add on window resize event handler :
var menu = jQuery('.nav .wrap > ul');
jQuery(".toggle").click(function() {
menu.slideToggle(500);
});
jQuery(window).on('resize', function(){
if(!jQuery(".toggle").is(":visible") && !menu.is(':visible'))
{
menu.show();
}
});
jsFiddle : http://jsfiddle.net/3Jj7J/1/
update: this is alternative solution (just remove inline display property, so it will use css rule).
jQuery(window).on('resize', function(){
if(!jQuery(".toggle").is(":visible") && !menu.is(':visible'))
{
menu.css({'display':''});
}
});
DEMO
I've read your problem and tested it myself. Now, I've made the Link appear by doing the following:
CSS:
#bar {
display: none;
color: #000000;
border: 1px solid red;
}
#media screen and (max-width: 500px) {
#nav ul {
display: none;
}
.bar{
display: block;
}
}
To see for yourself (http://jsfiddle.net/hSZ7t/) What I've done is changed your CSS. Instead of you using:
.bar {
It's now:
#bar {

Categories

Resources