10

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 href="#" class="close" data-toggle="offcanvas" data-target=".navmenu">&times;</a>
 <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 <a href="http://www.jasny.net/bootstrap/examples/navmenu-push">push effect</a> and <a href="http://www.jasny.net/bootstrap/examples/navmenu-reveal">reveal effect</a>.</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;
    }
ChosenJuan
  • 821
  • 2
  • 18
  • 46
  • 2
    I don't fully understand what the problem is and what you're trying to do. Can you boil it down a little bit? – Serg Chernata Feb 05 '17 at 13:28
  • I'm trying to make the bootstrap scroll spy activate when the browser width is 992px or greater, but when you reduce the screen size to 991px width or less the bootstrap scroll-spy should be removed or it will in cause issues with my off-canvas menu. – ChosenJuan Feb 06 '17 at 16:53

3 Answers3

1

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');
  }
}
Serg Chernata
  • 11,774
  • 6
  • 30
  • 49
  • Where do I add the class on the html? I tried:
    – ChosenJuan Feb 06 '17 at 17:43
  • You don't, `js` does it for you. – Serg Chernata Feb 06 '17 at 17:43
  • It's not working, the off-canvas menu doesn't close and navigate to the anchor link: https://jsfiddle.net/mchwy9Lx/ – ChosenJuan Feb 06 '17 at 18:11
  • Pay attention to errors in console. Fiddle wraps everything into a document load event as it is, so that initial block in your js was unnecessary. I also changed the logic in function at the very end from !! to !. The logic works now, you can see the class being toggled on #myScrollspy during window resize. The rest of your js is a bit of mystery to me. – Serg Chernata Feb 06 '17 at 18:20
  • When I removed the "!" and left only one "!" It causes an issue that won't navigate to the anchor link properly on a screen width greater than 992px. Can you update the fiddle? Whatever you told me to do is not working – ChosenJuan Feb 06 '17 at 19:20
  • The fiddle doesn't work: The anchor link should highlight the menu and navigate to the desired section when I click on it, not just when I scroll by it, And when the screen size is less than 992px the off screen menu should close. Check the original fiddle without the scrollspy: https://jsfiddle.net/kmdLg7t0/ – ChosenJuan Feb 06 '17 at 20:45
1

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.

Atul Jindal
  • 910
  • 6
  • 8
1

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.

jetpackpony
  • 1,195
  • 1
  • 9
  • 22
  • It's almost perfect but there's an issue that wasn't there before. When you click on the off-canvas menu, and resize the browser width to >992px, and then resize the browser again to <992px the black overlay pops up without the off-canvas menu: i.is.cc/1XBb6tVg.png – ChosenJuan Feb 12 '17 at 13:42
  • @ChosenJuan you can try and create a workaround via `window.resize` event. If I were you I wouldn't worry too much about browser resizing. You as a developer do that a lot, but most users will rarely resize their browser windows. – jetpackpony Feb 12 '17 at 17:17
  • The original windows resize workaround from my original fiddle: https://jsfiddle.net/kmdLg7t0/ doesn't work on the new fiddle. Can you please show me how to fix this problem? I will add an extra bounty when you update your answer with the fiddle since the original bounty expired. – ChosenJuan Feb 12 '17 at 22:06
  • @ChosenJuan I've updated the fiddle. https://jsfiddle.net/kmdLg7t0/5/ Basically on window resize it checks if the window is over the breakpoint and if it is, it removes the backdrop, since we should never have a backdrop on a wider screen – jetpackpony Feb 13 '17 at 11:32
  • Perfect! Thank you for your answer, and for shortening my js code. :) – ChosenJuan Feb 13 '17 at 23:21