Jquery script for submenu, how can i fix it? - javascript

This code works great if i want to make toggle effect by clicking on the menu. You can see the result here.
$(document).ready(function () {
var $menu2 = $("#menu-2");
var $links = $('#menu-menu-1 .menu-item a').click(function () {
var submenu = $(this).next();
$subs.not(submenu).hide();
var isVisible = submenu.stop(true, true).is(':visible');
$menu2.stop(true, true);
if (isVisible) {
submenu.hide(500);
$menu2.slideUp(300);
} else {
$menu2.slideUp(300, function () {
$menu2.slideDown(300);
submenu.show(500);
});
}
});
var $subs = $links.next();
});
My problem is .menu-item a, with this the script executes the code for all links of the menu(also for links that don't have submenu links). But in my case i want to execute only for the links that contains submenu links.
If i try t replace .menu-item a with .menu > ul li a doesn't work.
The structure of the html(generated by php) code is like this:
<ul id="menu-menu-1">
<li class="menu">
<a>News</a> //first main menu
<ul class="sub-menu" style="display: none;"> //second grey menu
<li>
<a>Mondo</a>
<a>News Live</a>
<a>Quotidiani Cartacei</a>
</li>
</li>
</ul>
The relevant css:
.menu {
background-color: #F6F6EE;
border-radius: 1px;
height: 30px;
padding: 10px 10px 0 5px;
width: 100%;
}
.menu li a {
color: #716B6B;
display: block;
float: left;
font-size: 14px;
padding: 2px 17px;
text-decoration: none;
width: 100%;
}
#menu-2 {
background-color: #DCDCD5;
border-radius: 1px;
display: none;
height: 33px;
position: relative;
width: 100%;
z-index: -1;
}
How can i fix it?

You need to check, on the top of the function, if the current link has a submenu attached. Check the first two lines of the click callback:
$(document).ready(function () {
var $menu2 = $("#menu-2");
var $links = $('#menu-menu-1 .menu-item a').click(function () {
var submenu = $(this).next(".sub-menu");
var hasSubmenu = submenu.length >= 1;
if(hasSubmenu) {
handleLinkWithSubmenu.call(this, submenu);
} else {
handleLinkWithoutSubmenu.call(this);
}
});
var $subs = $links.next();
function handleLinkWithSubmenu(submenu) {
$subs.not(submenu).hide();
var isVisible = submenu.stop(true, true).is(':visible');
$menu2.stop(true, true);
if (isVisible) {
submenu.hide(500);
$menu2.slideUp(300);
} else {
$menu2.slideUp(300, function () {
$menu2.slideDown(300);
submenu.show(500);
});
}
}
function handleLinkWithoutSubmenu() {
$subs.hide();
}
});

If I understand your question correctly (only execute if selector has children), then replace with this:
var $links = $('#menu-menu-1 .menu-item a').click(function (e) {
if($links.children().length > 0){
e.preventDefault();
var submenu = $(this).next();
$subs.not(submenu).hide();
var isVisible = submenu.stop(true, true).is(':visible');
$menu2.stop(true, true);
if (isVisible) {
submenu.hide(500);
$menu2.slideUp(300);
} else {
$menu2.slideUp(300, function () {
$menu2.slideDown(300);
submenu.show(500);
});
}
}
});

I think something like this will work for what you need, it's a lot cleaner and more compact:
UPDATED:
http://jsfiddle.net/FZHC8/2/
<head>
<script src="jquery-1.10.2.min.js"></script>
<style>
.menu {
background-color: #F6F6EE;
border-radius: 1px;
height: 30px;
padding: 10px 10px 0 5px;
width: 100%;
}
a {
color: #716B6B;
display: block;
float: left;
font-size: 14px;
padding: 2px 17px;
width: 100%;
cursor:pointer;
}
#menu-2 {
background-color: #DCDCD5;
border-radius: 1px;
height: 33px;
position: relative;
width: 100%;
z-index: -1;
}
li{
list-style:none;
}
</style>
</head>
<body>
<ul id="menu-menu-1">
<li class="menu">
<a>News</a>
<ul class="sub-menu">
<li>
<a>Mondo</a>
<a>News Live</a>
<a>Quotidiani Cartacei</a>
</li>
</ul>
<a>News1</a>
<ul class="sub-menu">
<li>
<a>Mondo1</a>
<a>News Live1</a>
<a>Quotidiani Cartacei1</a>
</li>
</ul>
</li>
</ul>
<script>
(function () {
$("li > ul").hide();
$(".menu > a").click(function (e) {
$(this).parent().children(".sub-menu").slideUp(200);
$(this).next(".sub-menu").slideDown(200);
})
})();
</script>
</body>

Related

click button scroll to specific div

I have a page that has a fixed menu and content box(div).
When click the menu, content box scroll to specific div.
So far so good.
This is the sample here.
https://jsfiddle.net/ezrinn/8cdjsmb9/11/
The problem is when I wrap this whole div and, make them as show/hide toggle button, the scroll is not working.
This is the sample that not working.
https://jsfiddle.net/ezrinn/8cdjsmb9/10/
Also here is the snippet
$('.btn').click(function() {
$(".wrap").toggleClass('on');
});
var div_parent_class_name;
var divs_class;
var id_offset_map = {};
$(document).ready(function() {
div_parent_class_name = "wrap_scroll";
divs_class = "page-section";
var scroll_divs = $("." + div_parent_class_name).children();
id_offset_map.first = 0;
scroll_divs.each(function(index) {
id_offset_map["#" + scroll_divs[index].id] = scroll_divs[index].offsetTop
});
$('a').bind('click', function(e) {
e.preventDefault();
var target = $(this).attr("href")
$('.wrap_scroll').stop().animate({
scrollTop: id_offset_map[target]
}, 600, function() {
/* location.hash = target-20; */ //attach the hash (#jumptarget) to the pageurl
});
return false;
});
});
$(".wrap_scroll").scroll(function() {
var scrollPos = $(".wrap_scroll").scrollTop();
$("." + divs_class).each(function(i) {
var divs = $("." + divs_class);
divs.each(function(idx) {
if (scrollPos >= id_offset_map["#" + this.id]) {
$('.menu>ul>li a.active').removeClass('active');
$('.menu>ul>li a').eq(idx).addClass('active');
}
});
});
}).scroll();
body,
html {
margin: 0;
padding: 0;
height: 3000px;
}
.wrap { display:none;}
.wrap.on { display:block;}
.menu {
width: 100px;
position: fixed;
top: 40px;
left: 10px;
}
.menu a.active {
background: red
}
.wrap_scroll {
position: absolute;
top: 20px;
left: 150px;
width: 500px;
height: 500px;
overflow-y: scroll
}
#home {
background-color: #286090;
height: 200px;
}
#portfolio {
background: gray;
height: 600px;
}
#about {
background-color: blue;
height: 800px;
}
#contact {
background: yellow;
height: 1000px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="btn">show/hide</button>
<div class="wrap">
<div class="menu">
<ul>
<li><a class="active" href="#home">Home</a> </li>
<li>Portfolio </li>
<li>About </li>
<li>Contact </li>
</ul>a
</div>
<div class="wrap_scroll">
<div class="page-section" id="home">hh</div>
<div class="page-section" id="portfolio">pp</div>
<div class="page-section" id="about">aa</div>
<div class="page-section" id="contact">cc</div>
</div>
</div>
What Do I need to fix the code? please help.
When you calculate your offset, the div is hidden with display: none. This results in the offsets being set/calculated to zero.
Here's a quick fix I threw together: https://jsfiddle.net/hrb58zae/
Basically, moved the logic to determine offset after clicking show/hide.
var setOffset = null;
...
if (!setOffset) {
var scroll_divs = $("." + div_parent_class_name).children();
id_offset_map.first = 0;
scroll_divs.each(function(index) {
id_offset_map["#" + scroll_divs[index].id] = scroll_divs[index].offsetTop
});
setOffset = true;
}
In your CSS, instead of using display: none and display: block, try using visible instead:
.wrap { visibility:hidden;}
.wrap.on { visibility:visible;}
This will hide the element without affecting the layout.
Updated fiddle: https://jsfiddle.net/a5u683es/
The problem was you are trying to update id_offset_map when content was hidden. When you use 'display:none' prop you won't get dimensions for that element and so its not working.
I updated the logic please check the fiddle https://jsfiddle.net/qfrsmnh5/
var id_offset_map = {};
var div_parent_class_name = "wrap_scroll";
var divs_class = "page-section";
var scroll_divs = $("." + div_parent_class_name).children();
function updateOffsets(){
id_offset_map.first = 0;
scroll_divs.each(function(index) {
id_offset_map["#" + scroll_divs[index].id] = scroll_divs[index].offsetTop
});
}
$(document).ready(function() {
$('.btn').click(function() {
$(".wrap").toggleClass('on');
if($(".wrap").hasClass("on")){
updateOffsets();
}
});
$('a').on('click', function(e) {
e.preventDefault();
var target = $(this).attr("href")
$('.wrap_scroll').stop().animate({
scrollTop: id_offset_map[target]
}, 600, function() {
/* location.hash = target-20; */ //attach the hash (#jumptarget) to the pageurl
});
return false;
});
});
$(".wrap_scroll").on('scroll',function() {
var scrollPos = $(".wrap_scroll").scrollTop();
$("." + divs_class).each(function(i) {
var divs = $("." + divs_class);
divs.each(function(idx) {
if (scrollPos >= id_offset_map["#" + this.id]) {
$('.menu>ul>li a.active').removeClass('active');
$('.menu>ul>li a').eq(idx).addClass('active');
}
});
});
}).scroll();
body,
html {
margin: 0;
padding: 0;
height: 3000px;
}
.wrap { display:none;}
.wrap.on { display:block;}
.menu {
width: 100px;
position: fixed;
top: 40px;
left: 10px;
}
.menu a.active {
background: red;
}
.wrap_scroll {
position: absolute;
top: 20px;
left: 150px;
width: 500px;
height: 500px;
overflow-y: scroll;
}
#home {
background-color: #286090;
height: 200px;
}
#portfolio {
background: gray;
height: 600px;
}
#about {
background-color: blue;
height: 800px;
}
#contact {
background: yellow;
height: 1000px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="btn">show/hide</button>
<div class="wrap">
<div class="menu">
<ul>
<li><a class="active" href="#home">Home</a></li>
<li>Portfolio </li>
<li>About </li>
<li>Contact </li>
</ul>
</div>
<div class="wrap_scroll">
<div class="page-section" id="home">hh</div>
<div class="page-section" id="portfolio">pp</div>
<div class="page-section" id="about">aa</div>
<div class="page-section" id="contact">cc</div>
</div>
</div>
works perfectly, it's just that when you use display: none you can not do the offsetTop calculations because in fact the element is not rendered, I'm not sure if all the values ​​give 0 or undefined, I guess undefined, a solution is always calculate Positions using a function:
var div_parent_class_name;
var divs_class;
var id_offset_map = {};
function calcTops(){
div_parent_class_name = "wrap_scroll";
divs_class = "page-section";
var scroll_divs = $("." + div_parent_class_name).children();
id_offset_map.first = 0;
scroll_divs.each(function(index) {
id_offset_map["#" + scroll_divs[index].id] = scroll_divs[index].offsetTop
});
}
https://jsfiddle.net/561oe7rb/1/
is not the optimal way, but it is to give you an idea. Sorry for my English.
Just Checkout This Working page I have designed
jQuery(document).on('scroll', function(){
onScroll();
});
jQuery(document).ready(function($) {
div_slider();
showhide();
});
/*show hide content*/
function showhide(){
$('.toggle-wrapper button').on('click', function(){
$('.wrapper').toggle();
// div_slider();
})
}
/*scrolling page on header elements click*/
function div_slider(){
$('ul li a').on('click', function(e){
e.preventDefault();
$('ul li a').removeClass('active');
$(this).addClass('active');
var attrval = $(this.getAttribute('href'));
$('html,body').stop().animate({
scrollTop: attrval.offset().top
}, 1000)
});
}
/*adding active class on header elements on page scroll*/
function onScroll(event){
var scrollPosition = $(document).scrollTop();
$('ul li a').each(function () {
var scroll_link = $(this);
var ref_scroll_Link = $(scroll_link.attr("href"));
if (ref_scroll_Link.position().top <= scrollPosition && ref_scroll_Link.position().top + ref_scroll_Link.height() > scrollPosition) {
$('ul li a').removeClass("active");
scroll_link.addClass("active");
}
else{
scroll_link.removeClass("active");
}
});
}
body {
margin: 0;
}
.toggle-wrapper {
position: fixed;
top: 0;
left: 0;
right: 0;
background-color: #ccd2cc;
text-align: center;
}
.toggle-wrapper button {
background-color: #ED4C67;
color: #ffffff;
padding: 10px 20px;
border: 0;
cursor: pointer;
border-radius: 5px;
}
.toggle-wrapper button:active{
background-color: #B53471;
}
header {
background-color: #6C5CE7;
position: fixed;
top: 36px;
z-index: 99;
left: 0;
right: 0;
}
header ul {
list-style: none;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0;
margin: 0;
}
ul li {
flex: 1 100%;
display: flex;
justify-content: center;
}
.wrapper {
margin-top: 36px;
}
header a {
color: #ffffff;
padding: 15px;
display: block;
text-decoration: navajowhite;
text-transform: uppercase;
width: 100%;
text-align: center;
}
header a.active {
color: #000000;
background-color: #ffffff;
}
section {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
section.section1 {
background-color: #FFEAA7;
}
section.section2{
background-color:#FAB1A0;
}
section.section3{
background-color:#7F8C8D;
}
section.section4{
background-color:#4CD137;
}
section.section5{
background-color:#A3CB38;
}
section.section6{
background-color:#70A1FF;
}
section.section7{
background-color:#079992;
}
<div class="toggle-wrapper">
<button>Toggle</button>
</div>
<div class="wrapper" style="display: none;">
<header>
<ul>
<li><a class="active" href="#one">one</a></li>
<li>two</li>
<li>three</li>
<li>four</li>
<li>five</li>
<li>six</li>
<li>seven</li>
</ul>
</header>
<section class="section1" id="one">SECTION ONE</section>
<section class="section2" id="two">SECTION TWO</section>
<section class="section3" id="three">SECTION THREE</section>
<section class="section4" id="four">SECTION FOUR</section>
<section class="section5" id="five">SECTION FIVE</section>
<section class="section6" id="six">SECTION SIX</section>
<section class="section7" id="seven">SECTION SEVEN</section>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Javascript Dropdown Staying open

In my site I made a simple dropdown menu, but my problem is that it won't close if mouseleave happens on the <span> that triggers the dropdown.
Here is my code:
//Find the dropdown span
var header = document.getElementById('drop');
//Find the ul with the links
var ul = document.getElementById('nav-dropdown');
//Get the width and apply it to the dropdown items
var width = drop.getBoundingClientRect().width;
ul.style.minWidth = width + "px";
//Round the corners on the last link
var links = document.getElementsByClassName('dropdown-link');
links[links.length - 1].style.borderRadius = "0 0 7px 7px";
var open = 0;
//Onhover, display the dropdown;
header.addEventListener("mouseover", function() {
ul.style.display = "block";
header.style.borderRadius = "7px 7px 0 0";
if (links[0].getBoundingClientRect().width > width) {
links[0].style.borderRadius = "0 7px 0 0";
}
open = 1;
});
//When the mouse leaves the menu, close it.
ul.addEventListener("mouseleave", function() {
ul.style.display = "none";
header.style.borderRadius = "7px";
open = 0;
});
//What I've tried to fix it:
/*
header.addEventListener("mouseleave", function() {
ul.style.display = "none";
header.style.borderRadius = "7px";
});
*/
/*Stylesheet for this stuff*/
* {
font-family: arial;
margin: 0;
padding: 0;
box-sizing: border-box;
font-size: 20px;
text-decoration: none;
list-style: none;
}
a:visited {
color: white;
}
a,
#drop {
color: white;
}
a:hover {
color: coral;
}
.header-links-container {
position: relative;
top: 0;
background: rgb(63, 83, 95);
width: 100%;
height: 80px;
opacity: .8;
z-index: 999;
}
.title {
font-weight: bold;
font-size: 30px;
padding: 20px 50px;
position: relative;
float: left;
color: white;
}
.header-links {
position: relative;
float: right;
vertical-align: middle;
}
.nav-links {
margin: auto;
padding-top: 20px;
padding-right: 30px;
}
.nav-link {
position: relative;
float: right;
padding: 0 20px;
font-size: 23px;
padding: 5px 10px;
margin: 5px;
background: #4471ba;
border-radius: 7px;
}
.nav-link:hover {
background: #4480ba;
color: #d1d1d1;
}
#nav-dropdown {
display: none;
margin-top: 42px;
margin-left: 5px;
position: absolute;
}
.dropdown-link {
color: black;
background-color: #ccc;
padding: 5px 10px;
}
.dropdown-link:hover {
color: #000;
background-color: #a7a7a7;
}
.dropdown-link:active {
color: white;
background-color: #3b8cfa;
}
<div class="header-links-container">
<h2 class="title">Title</h2>
<div class="header-links">
<ul class="nav-links">
<li class="nav-link">Photo Gallery</li>
<li class="nav-link">SLAP</li>
<li id="drop" class="nav-link"><span>Dropdown</span></li>
<ul id="nav-dropdown" class="jim">
<a href="#">
<li class="dropdown-link">Link 1</li>
</a>
<a href="#">
<li class="dropdown-link">Link 2</li>
</a>
<a href="#">
<li class="dropdown-link">Longer Link</li>
</a>
<a href="#">
<li class="dropdown-link">Vacuum</li>
</a>
</ul>
</ul>
</div>
</div>
<p>
Relavent JS lines start at Line 16
</p>
And here is the fiddle that might make more sense: https://jsfiddle.net/dLw1hu5n/6/
I've tried closing the dropdown like in the last code block, but then it won't stay open when you go to hover over the links. I've also tried making the menu close when the mouse hovers over the navbar div, but no luck there either.
Can I fix this or do I need to start from square 1?
I would prefere to solve this via css. However, in your case you can try the following:
function displayDropdown() {
ul.style.display = "block";
header.style.borderRadius = "7px 7px 0 0";
if (links[0].getBoundingClientRect().width > width) {
links[0].style.borderRadius = "0 7px 0 0";
}
open = 1;
}
function hideDropdown() {
ul.style.display = "none";
header.style.borderRadius = "7px";
open = 0;
}
//Onhover, display the dropdown;
header.addEventListener("mouseover", function() {
displayDropdown();
});
ul.addEventListener("mouseover", function() {
displayDropdown();
});
//When the mouse leaves the menu, close it.
ul.addEventListener("mouseleave", function() {
hideDropdown();
});
header.addEventListener("mouseleave", function() {
hideDropdown();
});
Your JS is fine but your event listener for mouseleave needs to be on the enclosing div. This way your element stays open until you hover outside of the header
t.addEventListener("mouseleave", function() {
ul.style.display = "none";
header.style.borderRadius = "7px";
open = 0;
});
What is t?
var t = document.getElementById(t);
What element has id T?
Try this fiddle to find out https://jsfiddle.net/dLw1hu5n/12/

How do I add a fade in on click to a javascript dropdown?

I was using this: https://gist.github.com/nguyenning/f99b38ec9ccdf3b73732 and easily created a dropdown menu that works great but I want to have the menu Fade in and out when it is clicked. This is what I tried but the fadeIn is not working:
$(function () {
var $overlay = $('.overlay');
var $toggle = $('.toggle-menu');
var toggleOverlay = function (evt) {
if (!$(evt.target).closest($overlay).length) {
$overlay.addClass('hidden');
} else {
$(document).one('click', toggleOverlay) .fadeIn(1000);
}
}
$toggle.click(function (evt) {
evt.preventDefault();
evt.stopPropagation();
$overlay.toggleClass('hidden');
$(document).one('click', toggleOverlay) .fadeIn(1000);
});
});
The .slideToggle() function works. Is it what you want?
$(function () {
var $overlay = $('.overlay');
var $toggle = $('.toggle-menu');
$toggle.click(function () {
$overlay.slideToggle();
});
});
.hidden {
display: none;
}
.container {
width: 300px;
padding: 16px;
margin: auto;
position: relative;
border-radius: 2px;
border: 1px solid #ccc;
}
.overlay {
position: absolute;
border-radius: 2px;
border: 1px solid #ccc;
}
<div class="container">
Toggle Menu
<div class="overlay hidden">
<ul>
<li>
Menu 1
</li>
<li>
Menu 2
</li>
<li>
Menu 3
</li>
</ul>
</div>
</div>
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="script.js"></script>

Highlighting links based on scroll position

I'm trying to get the my links to highlight if the user is scrolling over the page that is for that link. But for some reason it isn't working properly. I Have commented out my first try in jquery and tried again but link two is highlighting when link one should.
<nav>
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
</ul>
<p></p>
</nav>
<div id="sec_one" class="sections">
</div>
<div id="sec_two" class="sections">
</div>
<div id="sec_three" class="sections">
</div>
<script
src="https://code.jquery.com/jquery-3.2.1.min.js"
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
crossorigin="anonymous"></script>
*{
margin: 0;
padding: 0;
}
nav{
width: 100%;
background-color: black;
position: fixed;
top: 0;
}
nav ul{
width: 50%;
margin: 0 auto;
list-style-type: none;
text-align: center;
}
nav ul li{
display: inline;
width: 100%;
}
nav ul li a{
font-size: 40px;
color: white;
text-decoration: none;
}
nav ul li a{
}
.sections{
width: 100%;
height: 2000px;
}
#sec_one{
background-color: blue;
}
#sec_two{
background-color: red;
}
#sec_three{
background-color: yellow;
}
.active{
background-color: #666666;
}
p{
color: white;
}
$(window).scroll(function(){
var scrollPos = $(window).scrollTop();
var page1Top = $("#sec_one").scrollTop();
var page1Bot = $("#sec_one").outerHeight();
var page2Top = $("#sec_two").scrollTop();
var page2Bot = $("#sec_two").outerHeight();
var page3Top = $("#sec_three").scrollTop();
var page3Bot = $("#sec_three").outerHeight();
/*if(scrollPos >= page1Top && scrollPos < page1Bot){
$("#link_1").addClass("active");
$("#link_2").removeClass("active");
$("#link_3").removeClass("active");
}else if(scrollPos >= page2Top && scrollPos < page2Bot){
$("#link_1").removeClass("active");
$("#link_3").removeClass("active");
$("#link_2").addClass("active");
}else if(scrollPos >= page3Top && scrollPos < page3Bot){
$("#link_3").addClass("active");
$("#link_1").removeClass("active");
$("#link_2").removeClass("active");
}*/
if(scrollPos >= page1Top && scrollPos < page1Bot){
$("#link_1").addClass("active");
$("#link_2").removeClass("active");
$("#link_3").removeClass("active");
}else {
$("#link_1").removeClass("active");
}
if(scrollPos >= page2Top && scrollPos < page2Bot){
$("#link_2").addClass("active");
$("#link_1").removeClass("active");
$("#link_3").removeClass("active");
}else {
$("#link_2").removeClass("active");
}
});
Your main problem is you're not using .offset() - With your code you're just getting the position relative to itself so the top always becomes 0 and bottom becomes 2000 - using offset would mean you're getting the position relative to the document so that it takes into account other elements as well.
Also you don't need to check the bottom location. You can just use the top location of the next section.
$(document).ready(function() {
$(window).scroll(function() {
var scrollPos = $(window).scrollTop();
var page1Top = $("#sec_one").offset().top;
var page2Top = $("#sec_two").offset().top;
var page3Top = $("#sec_three").offset().top;
if (scrollPos >= page1Top && scrollPos < page2Top) {
$("#link_1").addClass("active");
$("#link_2").removeClass("active");
$("#link_3").removeClass("active");
} else {
$("#link_1").removeClass("active");
}
if (scrollPos >= page2Top && scrollPos < page3Top) {
$("#link_2").addClass("active");
$("#link_1").removeClass("active");
$("#link_3").removeClass("active");
} else {
$("#link_2").removeClass("active");
}
if (scrollPos >= page3Top) {
$("#link_3").addClass("active");
$("#link_1").removeClass("active");
$("#link_2").removeClass("active");
} else {
$("#link_3").removeClass("active");
}
});
});
* {
margin: 0;
padding: 0;
}
nav {
width: 100%;
background-color: black;
position: fixed;
top: 0;
}
nav ul {
width: 50%;
margin: 0 auto;
list-style-type: none;
text-align: center;
}
nav ul li {
display: inline;
width: 100%;
}
nav ul li a {
font-size: 40px;
color: white;
text-decoration: none;
}
nav ul li a {}
.sections {
width: 100%;
height: 2000px;
}
#sec_one {
background-color: blue;
}
#sec_two {
background-color: red;
}
#sec_three {
background-color: yellow;
}
.active {
background-color: #666666;
}
p {
color: white;
}
<nav>
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
</ul>
<p></p>
</nav>
<div id="sec_one" class="sections"></div>
<div id="sec_two" class="sections"></div>
<div id="sec_three" class="sections"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Avoid the target of individual elements and the use of the rough ID's, you can evaluate the scrollTop and the offset().top of the element and based on the index of the section highlight the item you need:
$(window).scroll(function() {
var scrollPos = $(window).scrollTop(),
navH = $('nav').height();
$('.sections').each(function(i){
var offT = $(this).offset().top;
if((offT-scrollPos-navH) <= 0) {
$('.active').removeClass('active')
$('nav a').eq(i).addClass('active')
}
})
});
* { margin: 0; padding: 0;} nav { width: 100%; background-color: black; position: fixed; top: 0;} nav ul { width: 50%; margin: 0 auto; list-style-type: none; text-align: center;} nav ul li { display: inline; width: 100%;} nav ul li a { font-size: 40px; color: white; text-decoration: none;} .sections { width: 100%; height: 2000px;} #sec_one { background-color: blue;} #sec_two { background-color: red;} #sec_three { background-color: yellow;} .active { background-color: #666666;} p { color: white;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav>
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
</ul>
<p></p>
</nav>
<div id="sec_one" class="sections"></div>
<div id="sec_two" class="sections"></div>
<div id="sec_three" class="sections"></div>
So, I'm assuming you want the links to link to different divs on the page and not a completely different page.
You can use ScrollSpy to accomplish this really easily. The documentation can be found here.
Here's some sample code for how you would do this on your page:
First, reference the scrollspy.js file. Be sure to use the relative url depending on where you choose to save the file.
<script src="scrollspy.js"></script>
Then, in your page script file, you could have something like this.
$('.sections').on('scrollSpy:enter', function() {
switch($(this).attr('id')) {
case "sec_one":
$("#link_1").addClass("active");
$("#link_2").removeClass("active");
$("#link_3").removeClass("active");
break;
case "sec_two":
$("#link_1").removeClass("active");
$("#link_2").addClass("active");
$("#link_3").removeClass("active");
break;
case "sec_three":
$("#link_1").removeClass("active");
$("#link_2").removeClass("active");
$("#link_3").addClass("active");
break;
}
}
$('.sections').scrollSpy();
Rewrote this a bit so you aren't calculating each individual element in your JS and you can just find which div you've scrolled into dynamically and modify the class of the matching nav element.
var $sections = $('.sections'),
$lis = $('nav li');
$(window).on('scroll', function(){
var scrollPos = $(window).scrollTop(),
navHeight = $('nav').outerHeight();
$sections.each(function() {
var top = $(this).offset().top,
bottom = top + $(this).outerHeight();
if (scrollPos > top - navHeight && scrollPos < bottom) {
var $target = $lis.eq($(this).index() - 1);
$lis.not($target).removeClass('active');
$target.addClass('active');
}
})
});
*{
margin: 0;
padding: 0;
}
nav{
width: 100%;
background-color: black;
position: fixed;
top: 0;
}
nav ul{
width: 50%;
margin: 0 auto;
list-style-type: none;
text-align: center;
}
nav ul li{
display: inline-block;
}
nav ul li a{
font-size: 40px;
color: white;
text-decoration: none;
display: inline-block;
}
.sections{
height: 200vh;;
}
#sec_one{
background-color: blue;
}
#sec_two{
background-color: red;
}
#sec_three{
background-color: yellow;
}
.active{
background-color: #666666;
}
p{
color: white;
}
<nav>
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
</ul>
<p></p>
</nav>
<div id="sec_one" class="sections">
</div>
<div id="sec_two" class="sections">
</div>
<div id="sec_three" class="sections">
</div>
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>

Small bug with click-to-scroll menu in jQuery

On my website I have set up a "click-to-scroll" menu with the logic of:
1. when menu link is clicked, scroll to corresponding anchor and add active class to $(this)
2. onscroll, toggle active class according to the current anchor's location
This all works fine, but there is a small bug in that when you click a link, the page flickers slightly and so do the active menu links. You can see and test it live at http://jcwd98.appspot.com/ (warning that it's in its early development stages, no mobile and probably looks pretty crappy right now).
I'm not sure what causes the page to flicker, but I do know that the reason the menu links flicker is because my code is telling it to add an active class to it when it scrolls over its corresponding section. Since the document has to first scroll over a section to get to the desired section, it adds an active class to other links before it arrives.
I don't want either of these scenarios.
jsFiddle
Code:
var section_padding = 45;
$("#menu ul li a").on("click", function(event) {
event.preventDefault;
$("#menu ul li a.active").removeClass("active");
$(this).addClass("active");
var target = this.hash;
var menu = target;
var cache_target = $(target);
var buffer = (cache_target.offset().top - section_padding);
$("html, body").stop().animate({
"scrollTop": buffer
}, 400, "swing");
});
function scroll(event) {
var scroll_pos = $(document).scrollTop();
$("#menu ul li a").each(function() {
var cur_link = $(this);
var ref_el = $(cur_link.attr("href"));
if( ref_el.position().top <= scroll_pos && ref_el.position().top + ref_el.height() + (section_padding * 2) > scroll_pos ) {
$("#menu ul li a").removeClass("active");
cur_link.addClass("active");
} else {
cur_link.removeClass("active");
}
});
}
$(document).on("scroll", scroll);
* {
margin: 0;
padding: 0;
}
#menu {
display: block;
height: 50px;
width: 100%;
position: fixed;
z-index: 100;
background: rgba(255, 255, 255, .8);
}
#menu ul {
display: block;
width: 100%;
height: 100%;
list-style: none;
}
#menu ul li {
display: block;
box-sizing: border-box;
height: 100%;
width: calc(100% / 5);
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
float: left;
text-align: center;
line-height: 50px;
}
#menu ul li a {
display: block;
text-decoration: none;
font-family: arial;
}
#menu ul li a:hover,
#menu ul li a.active {
background: #f0f0f0;
}
#sections {
display: block;
position: relative;
top: 50px;
}
section {
display: block;
height: 500px;
width: 100%;
background: #67D182;
padding: 45px 50px;
box-sizing: border-box;
}
#sections section:nth-child(even) {
background: #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<nav id="menu">
<ul>
<li>Top</li>
<li>About</li>
<li>Portfolio</li>
<li>Contact</li>
<li>Blog</li>
</ul>
</nav>
<div id="sections">
<section id="top">
<h2>#top</h2>
</section>
<section id="about">
<h2>#about</h2>
</section>
<section id="portfolio">
<h2>#portfolio</h2>
</section>
<section id="contact">
<h2>#contact</h2>
</section>
<section id="blog">
<h2>#blog</h2>
</section>
</div>
Any help would be greatly appreciated. :)
This happen because preventDefault is a function, then you only need to change:
event.preventDefault;
To:
event.preventDefault();
And this work fine.
FIDDLE: https://jsfiddle.net/lmgonzalves/ve5qr3bL/2/
EDIT:
You need to unbind the scroll event, and then bind it again when the animation be completed.
$("#menu ul li a").on("click", function(event) {
event.preventDefault();
$(document).off("scroll"); // here unbind
// code
$("html, body").stop().animate({
"scrollTop": buffer
}, 400, "swing", function() {
$(document).on("scroll", scroll); // here bind again
});
});
FIDDLE: https://jsfiddle.net/lmgonzalves/ve5qr3bL/3/

Categories

Resources