I am using a bootstrap navbar, and above the navigation links I have some text which I want to hide when the user scrolls down. Also, i want the navbar to be fixed top.
I am using a slideUp() and slideDown() to hide/show the text above the navbar, and I am using Jquery animate() to modify the navbar's height. (I need to have it's height explicit because of CSS reasons irrelevant to this issue)
The problem is that when I scroll to the top, the animate() gets queued after the slideDown() (Maybe it is not queued but it has some unwanted delay), which does not happen in the scrolling-down case. I want them to be simultaneous.
Here is a JSFiddle with minimal code reproducing the problem.
Here is the relevant minimal code:
html:
<nav class="navbar navbar-default navbar-fixed-top">
<div class="header">
Atención 24 horas 0800-777-8101
</div>
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#myNavbar">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="brand" href="#"><img src="img/logo.jpg" class="logo" /></a>
</div>
<div class="collapse navbar-collapse" id="myNavbar">
<ul class="nav navbar-nav">
<li class="active">DSDSADSA</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li>ADSASD</li>
</ul>
</div>
</div>
</nav>
<div style="height:1000px;background-color:#ccc;padding:50px;"></div>
css:
.navbar {
height: 110px;
}
div.header{
text-align:right;
height:50px;
}
And the most important, Javascript:
$(document).ready(function(){
$(window).scroll(function() {
if ($(document).scrollTop() > 0) {
console.log('a');
$('.navbar').animate({height: '60px'});
$('div.header').slideUp();
} else {
console.log('b');
$('.navbar').animate({height: '110px'});
$('div.header').slideDown();
}
});
});
Your problem is, everytime you scroll when the scrollTop is greater than 0 it is applying:-
$('.navbar').animate({height: '110px'});
$('div.header').slideDown();
and not just when its up, (height and slideUp). This causes the else to fire multiple times. see the console for a
One way of fixing this is applying a class to .header to determine if you need to apply the animation. like so:-
$(document).ready(function(){
$(window).scroll(function() {
if ($(document).scrollTop() > 0 && !$('div.header').hasClass('hide')) {
console.log('a');
$('.navbar').animate({height: '60px'}, "fast");
$('div.header').slideUp("fast").toggleClass('hide');
} else if ($(document).scrollTop() == 0 && $('div.header').hasClass('hide')) {
console.log('b');
$('.navbar').animate({height: '110px'}, "fast");
$('div.header').slideDown("fast").toggleClass('hide');
}
});
});
Fiddle
or you could use data or a variable, etc.
Before you apply the animation or slideup make use of stop function to clear the queue.
$('div.header').stop();
$('.navbar').stop();
Check out the working demo for you code here JSfiddle
Determining if your .navbar is currently in the state of animation will do the trick, based on which you can decide NOT to animate.
Just add the following snippet which uses jQuery's is() method, and returns false if the .navbar is in the animation state:
if($('.navbar').is(':animated')){
return false;
}
JSFiddle
Related
I am making a website with a free template and i dont know JS really well. So i made a research and figured JS causing the problem.
So here is the relevant HTML and JS codes:
var clickMenu = function() {
$('#navbar a:not([class="external"])').click(function(event) {
var section = $(this).data('nav-section'),
navbar = $('#navbar');
if ($('[data-section="' + section + '"]').length) {
$('html, body').animate({
scrollTop: $('[data-section="' + section + '"]').offset().top - 55
}, 500);
}
if (navbar.is(':visible')) {
navbar.removeClass('in');
navbar.attr('aria-expanded', 'false');
$('.js-fh5co-nav-toggle').removeClass('active');
}
event.preventDefault();
return false;
});
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<!-- Mobile Toggle Menu Button -->
<i></i>
<a class="navbar-brand" href="index.html"><span>X</span>XXX XXXX</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li class="active"><span>Home</span></li>
<li><span>About</span></li>
<li><span>Services</span></li>
<li><span>Contact</span></li>
</ul>
</div>
</div>
</nav>
At the bottom of the function, the return false; statement is triggering on every link; regardless of whether it's an in page anchor, or linking to another page. This will prevent the browser from jumping to other pages.
I've changed your (relevant) code around some, and posted some new jQuery (with comments):
The target was removed from data-nav-section, and placed directly into the href. This reduces some superfluous code, as well as makes it so these will function on browsers without script support. Instead of a nice scroll, they will simply jump. But - since the JS is being used to stop the browser's functionality - browsers with script support will function fine.
I am using rel="external" instead of 'class="external", becauseclassis only useful to CSS;rel` can be used just as easily by CSS, but can also be interpreted by automated systems (like bots and search engines).
I've also replaced the class="active", with aria-current="page". Again, CSS works with both equally well, but aria-current="" works with accessible systems (i.e. screen readers).
NOTE: I am unsure what the if (navbar.is(':visible')) {... code block is for, so I have left it out of my example for now.
$('document').ready(() => {
$('#navbar a:not([rel="external"])').on('click', function(event) {
// Assign target element to the variable targetElement.
let targetElement = $("#" + $(this).attr('href').split('#')[1]);
// If the target element exists, it will have a length. If it doesn't exist, the browser will do its own thing.
if (targetElement.length) {
// Scroll browser window to the top of the element (-55px)
$('html, body').animate({
scrollTop: targetElement.offset().top - 55
}, 500);
// End funtion processing, and prevent the browser from performing any more actions.
return false;
}
})
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<!-- Mobile Toggle Menu Button -->
<i></i>
<a class="navbar-brand" href="index.html"><span>X</span>XXX XXXX</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li aria-current="page"><span>Home</span></li>
<li><span>About</span></li>
<li><span>Services</span></li>
<li><span>Contact</span></li>
</ul>
</div>
<div id="about" style="height: 200px; width=900px; background-color: #0F0;">
<h2>About</h2>
</div>
<div id="services" style="height: 200px; width=900px; background-color: #00F;">
<h2>Services</h2>
</div>
<div id="contact" style="height: 200px; width=900px; background-color: #F00;">
<h2>Contact</h2>
</div>
</div>
</nav>
I am using below JS code to fix header at the top for mobile only.
means if screen is scrolled for 80px css classes will be replaced.
working on android and PC but no luck on Ios.
any suggestions?
$(window).scroll(function () {
if ($(window).width() < 768) {
if ($(window).scrollTop() > 80) {
$('.navbar-right').addClass('custom-fixed-top');
}
else{
$('.navbar-right').removeClass('custom-fixed-top');
}
}
});
HTML CODE
<nav class="navbar navbar-default">
<div class="mid-container">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed hidden-xs" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#"><img src="images/logo.png" class="img-responsive" alt="logo"></a>
</div>
<div class="navbar-collapse collapse in" id="bs-example-navbar-collapse-1" aria-expanded="true" style="">
<ul class="nav navbar-nav navbar-right">
<li class="hidden-xs"><a class="hdr-orng-btn" href="#">受付時間</a></li>
<li class="hidden-xs"><a class="" href="#">10:00~19:00</a></li>
<li><a class="num" href="tel:03-0000-0000">TEL 03-0000-0000</a></li>
<li><a class="hdr-grn-btn" href="#contact_form">お問い合わせ</a></li>
</ul>
</div>
</div>
</div>
.navbar-nav.custom-fixed-top{
position: fixed;
top: 0;
width: 100%;
height: auto;
padding: 0 15px 15px;
margin: 0;
z-index: 2;
background-color:#fff;
left:0;
}
this is the code.
Ok this might seem far fetched but...
Try add the following to your CSS in your html tag if they're on IOS:
cursor: pointer;
Maybe can be your if verification of viewport screen width with 768, verify if you use some screen above that size.
I try replicate your code here, but $(window).scrollTop() works fancy here for me.
Image with your code on google chrome
Try changing this;
if ($(window).scrollTop() > 80) {...
To this;
if ($(body).scrollTop() > 80) {...
If that then works on ios, then that's the issue, so to cover both devices/browsers implement something like this;
var scrollentity = $('html,body');
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/))
{ scrollentity = $('body') }
else { scrollentity = $('html,body') }
scrollentity .scrollTop() > 80) {...
Hope it helps.
Question
How can I swap classes of my navbar using javascript.
Background
I have a fixed navbar that I am trying to change to a static navbar on smaller screen sizes (< 768px) because my mobile menu pusher is having layout problems due to the fixed nav.
HTML
<nav class="navbar navbar-default navbar-fixed-top" id="mynav">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-
toggle="collapse" data-target="#navbar" aria-expanded="false" aria-
controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Project name</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li class="active">Home</li>
<li>About</li>
<li>Products</li>
<li>Contact</li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
I tried creating a javascript function to add / remove the static nav class but for some reason it's not working.
JS
function changeNav() {
if ($(window).width() < 768) {
$("#mynav").addClass("navbar-static-top");
$("#mynav").removeClass("navbar-fixed-top");
} else {
$("#mynav").removeClass("navbar-static-top");
$("#mynav").addClass("navbar-fixed-top");
}
}
changeNav();
You can use this instead, this is propper and have better performances:
let lastState = false;
function checkForViewportChange () {
var state = window.matchMedia("(max-width: 768px)").matches;
if (state != lastState) {
if (state) {
//do your stuff here
} else {
//do your other stuff here
}
lastState = state
}}
window.setInterval (checkForViewportChange, 150);
You should check every time user resizes the screen, use .resize() method
Ex.
$(window).resize(() => {
let b = $(window).width() < 768;
$("#mynav").addClass("navbar-"+(b ? "static":"fixed")+"-top");
$("#mynav").removeClass("navbar-"+(b ? "fixed":"static")+"-top");
});
This is probably easier done with css media queries than javascript.
Example:
#media (max-width: 768px) {
// styles for device width <= 768px
}
If you really want to use javascript, then the answer from Abdeslem Charif is a good start
So recently I have implemented smooth scroll given by CSS-Tricks, and a solution to closing the navigation bar when an option has been clicked. The problem is, that without the bootstrap script for closing the navigation, the scroll works perfect and stops at the anchors accurately.
However, as soon as I add the code for the automatic bootstrap navigation closing, when I then proceed to click on the link, it goes down to the desired anchor, misses it, continues and then suddenly shoots back to the anchor.
What could be the problem?
I hope someone will be able to help me. I have attached the relevant pieces of code below.
Smooth Scroll provided by CSS Tricks:
<script>
.click(function(event) {
// On-page links
if (
location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '')
&&
location.hostname == this.hostname
){
// Figure out element to scroll to
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
// Does a scroll target exist?
if (target.length) {
// Only prevent default if animation is actually gonna happen
event.preventDefault();
$('html, body').animate({
scrollTop: target.offset().top
}, 1000, function() {
// Callback after animation
// Must change focus!
var $target = $(target);
$target.focus();
if ($target.is(":focus")) { // Checking if the target was focused
return false;
} else {
$target.attr('tabindex','-1'); // Adding tabindex for elements not focusable
$target.focus(); // Set focus again
};
});
}
}
});
</script>
JavaScript code to close the bootstrap 3.x navigation:
<script>
$('.navbar-collapse a').click(function(){
$(".navbar-collapse").collapse('hide');
});
</script>
My HTML code:
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<!-- Navigation logo and dropdown icon -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#" id="logo">LOGO</a>
</div>
<!-- Navigation Options -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right" id="nav-appearance">
<li id="main-nav">Home <span class="sr-only">(current)</span></li>
<li id="main-nav">About Us</li>
<li id="main-nav">Services</li>
<li id="main-nav">Gallery</li>
<li>Contact</li>
</ul>
</div>
</div>
</nav>
not 100% certain but it looks like the scroll script triggers and calculates the position in the document to scroll to and animates that over time provided.
Meanwhile your nav gets collapsed and removed from the document flow, meaning the calculation of position is now off so when the animation completes it has overshot desired position.
The scroll script then calls a callback function which gives focus to the target element, but as it is off the page giving focus causes it to snap back up the page so the focussed element is visible.
If you can call the scroll script after the bootstrap collapse script this should resolve the issue.
Question Background:
I have a standard bootstrap NavBar that collpases to a button on mobile devices.
The Issue:
When the user clicks on an item in the NavBar dropdown list the page will scroll down to the corrosponding div. I have a piece of JQuery that collpases the dropdown menu when the item has been clicks.
Without the JQuery to close the dropdown Nav menu the page scrolls down to the div with no issues. With the JQuery the page is scrolling down but stops well after the set 10px offset.
Without the JQuery menu closing code - working:
Note the small 10px above the panel item, this is what is wanted.
With the JQuery closing code - Broken:
Note that the page now scrolls down past the top of the div.
The Code:
The NavBar:
<div class="navbar">
<nav class="navbar navbar-default navbarColour" role="navigation" id="nav">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<img src="~/Images/DC.png" class="dc">
</div>
<div class="middleNavPadding">
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav pull-right">
<li>Services</li>
<li>Our Mission</li>
<li>Projects Gallery</li>
<li>Contact Us</li>
</ul>
</div>
</div>
</div>
</nav>
</div>
The JQuery Menu closing code:
$(document).ready(function () {
$(".navbar-nav .scroll-link").click(function (event) {
$(".navbar-collapse").collapse('hide');
});
});
The JQuery used to scroll to the Div as set in the NavBar menu:
$(document).ready(function () {
$('.scroll-link').on('click', function (event) {
event.preventDefault();
var sectionID = $(this).attr("data-id");
scrollToID('#' + sectionID, 750);
});
function scrollToID(id, speed) {
var offSet = 10;
var targetOffset = $(id).offset().top - offSet;
$('html,body').animate({ scrollTop: targetOffset }, speed);
}
});
The HTML Markup of the Panel:
<div id="Info">
//panel HTML
</div>
I believe the issue is that the JQuery is setting the 'top' of the page as the bottom of the dropdown menu which is then causing a false offset. Any help with solving a a solution to this would be much appreciated.
You are on the right track, just add the height of the #bs-example-navbar-collapse-1 to your offset.
function scrollToID(id, speed) {
var offSet = 10 + $('#bs-example-navbar-collapse-1').height(); // NAVBAR HEIGHT !!!
var targetOffset = $(id).offset().top - offSet;
$('html,body').animate({ scrollTop: targetOffset }, speed);
}
This should work.