I have a one-page website. When I scroll down I want my (fixed) navbar links to change the status to active when reaching the position of that specific div.
I used jQuery but it doesn't work. Here is my code:
// SMOOTH SCROLLING PAGES
$(document).ready(function () {
$(document).on("scroll", onScroll);
//smoothscroll
$('a[href^="#"]').on('click', function (e) {
e.preventDefault();
$(document).off("scroll");
$('a').each(function () {
$(this).removeClass('active');
})
$(this).addClass('active');
var target = this.hash,
menu = target;
$target = $(target);
$('html, body').stop().animate({
'scrollTop': $target.offset().top+2
}, 800, 'swing', function () {
window.location.hash = target;
$(document).on("scroll", onScroll);
});
});
});
function onScroll(event){
var scrollPos = $(document).scrollTop();
$('main-navigation a').each(function () {
var currLink = $(this);
var refElement = $(currLink.attr("href"));
if (refElement.position().top <= scrollPos && refElement.position().top + refElement.height() > scrollPos) {
$('main-navigation ul li a').removeClass("active");
currLink.addClass("active");
}
else{
currLink.removeClass("active");
}
});
};
Here is my HTML:
<nav id="main-navigation">
<ul>
<li class="active">Home</li>
<li>A</li>
<li>B</li>
<li>C</li>
<li>D</li>
</ul>
</nav>
<div id="a">DIV Alpha</div>
<div id="b">DIV Bravo</div>
<div id="c">DIV Charlie</div>
<div id="d">DIV Delta</div>
The smooth scrolling works perfectly, but when I scrolling back up from div #d the navbar li active state doesn't change.
Here, i prepare fiddle for you, i hope it what you looking for - http://jsfiddle.net/skyr9999/dpuvcj5w
<div class="content">
<div id="site-main">DIV Home</div>
<div id="a">DIV Alpha</div>
<div id="b">DIV Bravo</div>
<div id="c">DIV Charlie</div>
<div id="d">DIV Delta</div>
</div>
<nav id="main-navigation">
<ul>
<li><a class="menuitem" href="#site-main">Home</a></li>
<li><a class="menuitem" href="#a">A</a></li>
<li><a class="menuitem" href="#b">B</a></li>
<li><a class="menuitem" href="#c">C</a></li>
<li><a class="menuitem" href="#d">D</a></li>
</ul>
</nav>
js
$(document).ready(function () {
$('a[href^="#site-main"]').addClass('active');
//smoothscroll
$('.menuitem').on('click', function (e) {
e.preventDefault();
// $(document).off("scroll");
var athis = this;
var target = this.hash,
menu = target;
$target = $(target);
$('html, body').stop().animate({
'scrollTop': $target.offset().top + 2
}, 800, 'swing', function () {
window.location.hash = target;
$('.menuitem').removeClass('active');
$(athis).addClass('active');
});
});
$(window).scroll(function (event) {
var scrollPos = $(document).scrollTop();
if (scrollPos === 0)
{
$('a[href^="#site-main"]').addClass('active');
return;
}
$('.menuitem').each(function () {
var currLink = $(this);
var refElement = $(currLink.attr("href"));
if (refElement.position().top <= scrollPos && refElement.position().top + refElement.height() > scrollPos) {
$('.menuitem').removeClass("active");
currLink.addClass("active");
} else {
currLink.removeClass("active");
}
});
})
});
Because you did not include any css, i improvise a little, but it seems works just fine.
css
#a,#b,#c,#d,#site-main { height: 400px;}
#main-navigation {
position: fixed;
top: 0px;
right:10px;
}
#main-navigation ul li {
display: inline;
}
.active {
background: #f00;
}
Update 1
I update code to match fiddle now it change menu item selection when you scrool to item
You have to loop through with the list items instead and correct the id selector with a # notation:
$('#main-navigation li').each(function () { //<---correct id selector
var currLink = $(this);
var refElement = $(currLink.find('a').attr("href")); //<---find the anchor here
if (refElement.position().top <= scrollPos && refElement.position().top + refElement.height() > scrollPos) {
$('#main-navigation ul li').removeClass("active"); //<---and here
currLink.addClass("active");
}
else{
currLink.removeClass("active");
}
});
you can use this cool tiny plugin to achieve this
https://github.com/davist11/jQuery-One-Page-Nav
it will add current class when you are in a specific div or section
Hope this help :)
replace
$('a').each(function () {
$(this).removeClass('active');
})
$(this).addClass('active');
with
$('a').each(function () {
$(this).parent().removeClass('active');
})
$(this).parent().addClass('active');
complete code:
$('a[href^="#"]').on('click', function (e) {
e.preventDefault();
$(document).off("scroll");
$('a').each(function () {
$(this).parent().removeClass('active');
})
$(this).parent().addClass('active');
var target = this.hash,
menu = target;
$target = $(target);
$('html, body').stop().animate({
'scrollTop': $target.offset().top+2
}, 800, 'swing', function () {
window.location.hash = target;
});
});
No need of onScroll function
Related
I'm a new developer and still learning and i would appreciate a bit help in here coz I'm already insane.
I have this code, trying to make a dropdown with some options but i can't make it work. In inspector mode it says Uncaught ReferenceError, jQuery not defined it doesn't do the dropdown.
<div class="container">
<nav class="fixed-top nav-menu nav d-lg block justify-content-end">
<!--<div class="right">-->
<ul>
<li>
"Nome Utilizador"
</li>
<li class="drop-down">Action
<ul class="dropdown-menu">
<li class="dropdown-item">
exemplo
</li>
</ul>
</li>
</ul>
<!--</div>-->
</nav>
</div>
<script src="vendor/jquery/jquery.min.js"></script>
<script src="vendor/counterup/counterup.min.js"></script>
<script src="vendor/owl.carousel/owl.carousel.min.js"></script>
<script src="js/main.js"></script>
<script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="vendor/jquery.easing/jquery.easing.min.js"></script>
<script src="vendor/php-email-form/validate.js"></script>
<script src="vendor/waypoints/jquery.waypoints.min.js"></script>
<script src="vendor/venobox/venobox.min.js"></script>
<script src="vendor/isotope-layout/isotope.pkgd.min.js"></script>
<script src="vendor/aos/aos.js"></script>
This is the Main.js code
/**
* Template Name: OnePage - v2.2.2
* Template URL: https://bootstrapmade.com/onepage-multipurpose-bootstrap-template/
* Author: BootstrapMade.com
* License: https://bootstrapmade.com/license/
*/
!(function($) {
"use strict";
// Preloader
$(window).on('load', function() {
if ($('#preloader').length) {
$('#preloader').delay(100).fadeOut('slow', function() {
$(this).remove();
});
}
});
// Smooth scroll for the navigation menu and links with .scrollto classes
var scrolltoOffset = $('#header').outerHeight() - 2;
$(document).on('click', '.nav-menu a, .mobile-nav a, .scrollto', function(e) {
if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname) {
var target = $(this.hash);
if (target.length) {
e.preventDefault();
var scrollto = target.offset().top - scrolltoOffset;
if ($(this).attr("href") == '#header') {
scrollto = 0;
}
$('html, body').animate({
scrollTop: scrollto
}, 1500, 'easeInOutExpo');
if ($(this).parents('.nav-menu, .mobile-nav').length) {
$('.nav-menu .active, .mobile-nav .active').removeClass('active');
$(this).closest('li').addClass('active');
}
if ($('body').hasClass('mobile-nav-active')) {
$('body').removeClass('mobile-nav-active');
$('.mobile-nav-toggle i').toggleClass('icofont-navigation-menu icofont-close');
$('.mobile-nav-overly').fadeOut();
}
return false;
}
}
});
// Activate smooth scroll on page load with hash links in the url
$(document).ready(function() {
if (window.location.hash) {
var initial_nav = window.location.hash;
if ($(initial_nav).length) {
var scrollto = $(initial_nav).offset().top - scrolltoOffset;
$('html, body').animate({
scrollTop: scrollto
}, 1500, 'easeInOutExpo');
}
}
});
// Mobile Navigation
if ($('.nav-menu').length) {
var $mobile_nav = $('.nav-menu').clone().prop({
class: 'mobile-nav d-lg-none'
});
$('body').append($mobile_nav);
$('body').prepend('<button type="button" class="mobile-nav-toggle d-lg-none"><i class="icofont-navigation-menu"></i></button>');
$('body').append('<div class="mobile-nav-overly"></div>');
$(document).on('click', '.mobile-nav-toggle', function(e) {
$('body').toggleClass('mobile-nav-active');
$('.mobile-nav-toggle i').toggleClass('icofont-navigation-menu icofont-close');
$('.mobile-nav-overly').toggle();
});
$(document).on('click', '.mobile-nav .drop-down > a', function(e) {
e.preventDefault();
$(this).next().slideToggle(300);
$(this).parent().toggleClass('active');
});
$(document).click(function(e) {
var container = $(".mobile-nav, .mobile-nav-toggle");
if (!container.is(e.target) && container.has(e.target).length === 0) {
if ($('body').hasClass('mobile-nav-active')) {
$('body').removeClass('mobile-nav-active');
$('.mobile-nav-toggle i').toggleClass('icofont-navigation-menu icofont-close');
$('.mobile-nav-overly').fadeOut();
}
}
});
} else if ($(".mobile-nav, .mobile-nav-toggle").length) {
$(".mobile-nav, .mobile-nav-toggle").hide();
}
// Navigation active state on scroll
var nav_sections = $('section');
var main_nav = $('.nav-menu, #mobile-nav');
$(window).on('scroll', function() {
var cur_pos = $(this).scrollTop() + 200;
nav_sections.each(function() {
var top = $(this).offset().top,
bottom = top + $(this).outerHeight();
if (cur_pos >= top && cur_pos <= bottom) {
if (cur_pos <= bottom) {
main_nav.find('li').removeClass('active');
}
main_nav.find('a[href="#' + $(this).attr('id') + '"]').parent('li').addClass('active');
}
if (cur_pos < 300) {
$(".nav-menu ul:first li:first").addClass('active');
}
});
});
// Toggle .header-scrolled class to #header when page is scrolled
$(window).scroll(function() {
if ($(this).scrollTop() > 100) {
$('#header').addClass('header-scrolled');
} else {
$('#header').removeClass('header-scrolled');
}
});
if ($(window).scrollTop() > 100) {
$('#header').addClass('header-scrolled');
}
// Back to top button
$(window).scroll(function() {
if ($(this).scrollTop() > 100) {
$('.back-to-top').fadeIn('slow');
} else {
$('.back-to-top').fadeOut('slow');
}
});
$('.back-to-top').click(function() {
$('html, body').animate({
scrollTop: 0
}, 1500, 'easeInOutExpo');
return false;
});
// jQuery counterUp
$('[data-toggle="counter-up"]').counterUp({
delay: 10,
time: 1000
});
// Testimonials carousel (uses the Owl Carousel library)
$(".testimonials-carousel").owlCarousel({
autoplay: true,
dots: true,
loop: true,
responsive: {
0: {
items: 1
},
768: {
items: 2
},
900: {
items: 3
}
}
});
// Porfolio isotope and filter
$(window).on('load', function() {
var portfolioIsotope = $('.portfolio-container').isotope({
itemSelector: '.portfolio-item'
});
$('#portfolio-flters li').on('click', function() {
$("#portfolio-flters li").removeClass('filter-active');
$(this).addClass('filter-active');
portfolioIsotope.isotope({
filter: $(this).data('filter')
});
aos_init();
});
// Initiate venobox (lightbox feature used in portofilo)
$(document).ready(function() {
$('.venobox').venobox({
'share': false
});
});
});
// Portfolio details carousel
$(".portfolio-details-carousel").owlCarousel({
autoplay: true,
dots: true,
loop: true,
items: 1
});
// Init AOS
function aos_init() {
AOS.init({
duration: 1000,
once: true
});
}
$(window).on('load', function() {
aos_init();
});
})(jQuery);
First of all you need to include your jQuery script before your main.js file.
Also please consider in sharing your code here so we can help you better.
How is it possible to get the smooth scroll working more than once (with JQuery:s scrollTop)?
var projectContainer = document.getElementsByClassName('projectContainer')[0];
var position = $(window).scrollTop();
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll > position) {
$([document.documentElement, document.body]).animate({
scrollTop: $(".projectContainer").offset().top
}, 2000);
projectContainer.style.background = "orange";
$(window).bind("mousewheel", function() {
$("html, body").stop();
});
} else {
projectContainer.style.background = "white";
}
position = scroll;
});
.top {
height: 1000px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="top">Scroll</div>
<div class="projectContainer">hello</div>
I've tried this but it only works for the first time after running the script.
Thanks in advance!
I have a slider. If I upload photos using html, then everything works correctly. As soon as I try to upload files using Json, the block is stretched and the animation does not work smoothly, styles are no longer applied to it. Pictures are loaded cropped. Screenshots below. Where is the mistake?
https://ibb.co/FwbV9Gb
https://ibb.co/hLjpb5p
slider.js
var slideCount = $(".slider ul li").length;
var slideWidth = $(".slider ul li").width();
var slideHeight = $(".slider ul li").height();
var slideUlWidth = slideCount * slideWidth;
$(".slider").css({"max-width":slideWidth, "height": slideHeight});
$(".slider ul").css({"width":slideUlWidth, "margin-left": - slideWidth });
$(".slider ul li:last-child").prependTo($(".slider ul"));
function moveLeft() {
$(".slider ul").stop().animate({
left: + slideWidth
},700, function() {
$(".slider ul li:last-child").prependTo($(".slider ul"));
$(".slider ul").css("left","");
});
}
function moveRight() {
$(".slider ul").stop().animate({
left: - slideWidth
},700, function() {
$(".slider ul li:first-child").appendTo($(".slider ul"));
$(".slider ul").css("left","");
});
}
$(".next").on("click",function(){
moveRight();
});
$(".prev").on("click",function(){
moveLeft();
});
});
html
<div class="img">
<div class="slider">
Next
Prev
<ul id="sliders">
<li> <img src="/img/2.jpg"> </li>
<li><img src="/img/4.jpg"></li>
<li> <img src="/img/5.jpg"> </li>
<li> <img src="/img/6.jpg"> </li>
</ul>
</div>
</div>
add images using JSON (.js)
$(function() {
$.getJSON('catalog.json', function(data) {
$.each(data.catalog, function(i, category) {
const detail= category.details;
const image =detail.images;
const $slider=$(
"<li><img src="+image.one+"></li><li><img src="+image.two+"></li><li><img src="+image.three+"></li><li><img src="+image.four+"></li>"
)
console.log(image.one)
$slider.appendTo("#sliders");
});
});
The problem most likely lies in timing. On page load, all of the variables are calculated for widths etc.. However, your json takes a few seconds to load, therefore the sizes have already been calculated. What I did is create a function for slider creation and add that in the getJson success callback.
var slideCount;
var slideWidth;
var slideHeight;
var slideUlWidth;
$.create_slider = function(){
slideCount = $(".slider ul li").length;
slideWidth = $(".slider ul li").width();
slideHeight = $(".slider ul li").height();
slideUlWidth = slideCount * slideWidth;
$(".slider").css({"max-width":slideWidth, "height": slideHeight});
$(".slider ul").css({"width":slideUlWidth, "margin-left": - slideWidth });
$(".slider ul li:last-child").prependTo($(".slider ul"));
}
function moveLeft() {
$(".slider ul").stop().animate({
left: + slideWidth
},700, function() {
$(".slider ul li:last-child").prependTo($(".slider ul"));
$(".slider ul").css("left","");
});
}
function moveRight() {
$(".slider ul").stop().animate({
left: - slideWidth
},700, function() {
$(".slider ul li:first-child").appendTo($(".slider ul"));
$(".slider ul").css("left","");
});
}
$(".next").on("click",function(){
moveRight();
});
$(".prev").on("click",function(){
moveLeft();
});
});
$(function() {
$.getJSON('catalog.json', function(data) {
$.each(data.catalog, function(i, category) {
const detail= category.details;
const image =detail.images;
const $slider=$(
"<li><img src="+image.one+"></li><li><img src="+image.two+"></li><li><img src="+image.three+"></li><li><img src="+image.four+"></li>"
)
$slider.appendTo("#sliders");
});
$.create_slider();
});
I have script which indicates the current position in a menu and sets an active class on it. But I need specific rules for id contact_form. I need to add 1000px to the scrollTop value for that ID location.
Here is my code:
var lastId,
topMenu = $(".nav"),
topMenuHeight = topMenu.outerHeight()+15,
menuItems = topMenu.find("a"),
scrollItems = menuItems.map(function(){
var item = $($(this).attr("href"));
if (item.length) { return item; }
});
menuItems.click(function(e){
var href = $(this).attr("href"),
offsetTop = href === "#" ? 0 : $(href).offset().top-topMenuHeight-80+1;
$('html, body').stop().animate({
scrollTop: offsetTop
}, 300);
e.preventDefault();
});
$(window).scroll(function(){
var fromTop = $(this).scrollTop()+topMenuHeight+80;
var cur = scrollItems.map(function(){
if ($(this).offset().top < fromTop)
return this;
});
cur = cur[cur.length-1];
var id = cur && cur.length ? cur[0].id : "";
if (lastId !== id) {
lastId = id;
menuItems
.parent().removeClass("active")
.end().filter("[href='#"+id+"']").parent().addClass("active");
}
});
I would use jQuery's data method to embed the desired extra pixels into your HTML element. You can see this idea working in this fiddle or in the snippet below.
You could use this idea to control the point at which any element is considered "active", not just the contact form.
var lastId,
topMenu = $(".nav"),
topMenuHeight = topMenu.outerHeight()+15,
menuItems = topMenu.find("a"),
scrollItems = menuItems.map(function(){
var id = $(this).attr("href");
var item = $(id);
if (item.length) {
if (id === "#contact_form") { // Here we embed the desired extra fromTop value
item.data("extraTop", 1000);
}
return item;
}
});
menuItems.click(function(e){
var href = $(this).attr("href"),
offsetTop = href === "#" ? 0 : $(href).offset().top-topMenuHeight-80+1;
$('html, body').stop().animate({
scrollTop: offsetTop
}, 300);
e.preventDefault();
});
$(window).scroll(function(){
var $window = $(this);
var fromTop = $window.scrollTop()+topMenuHeight+80;
var cur = scrollItems.map(function(){
var $el = $(this);
var top = $el.offset().top;
var extra = $el.data("extraTop"); // Here we read the "extraTop" data attribute
if (!extra) // If this doesn't exist, we force it to be numeric 0
extra = 0;
if (top < fromTop + extra) // Now we compare to the old fromTop plus our extra top value
return this;
});
cur = cur[cur.length-1];
var id = cur && cur.length ? cur[0].id : "";
if (lastId !== id) {
lastId = id;
menuItems
.parent().removeClass("active")
.end().filter("[href='#"+id+"']").parent().addClass("active");
}
});
.active {
background: black;
color: white;
}
div {
height: 1300px;
}
.nav {
position: fixed;
top: 0;
height: auto;
}
.red {
background: #ff2a2a;
}
.green {
background: #33a033;
}
.blue {
background: #0080ff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="nav">
<li>Stuff</li>
<li>Stuff</li>
<li>Stuff</li>
<li>Contact Form</li>
<li>Stuff</li>
<li>Stuff</li>
<li>Other</li>
</ul>
<div id="stuff1" class="red">
Stuff
</div>
<div id="stuff2" class="red">
Stuff
</div>
<div id="stuff3" class="red">
Stuff
</div>
<div id="contact_form" class="green">
The contact: <input type="text" value="what?"/>
</div>
<div id="stuff4" class="red">
Stuff
</div>
<div id="stuff5" class="red">
Stuff
</div>
<div id="other" class="blue">
Other
</div>
I'm getting error from using top property in my jquery code. The code is about navigation dots on my slider. This is the link to my page: http://54.169.61.153/teavana
Below is my code. I'm getting error in the console saying that:
Uncaught TypeError: Cannot read property 'top' of undefined
$(document).ready(function () {
var one = $("#slider").offset();
var two = $("#tabsDiv").offset();
var three = $("#teaWare").offset();
var four = $("#videoo").offset();
$(window).scroll(function () {
var screenPosition = $(document).scrollTop();
if (screenPosition < one.top) {
$(".teavanamenu li a").removeClass("active");
$(".teavanamenu li a.menu1").addClass("active");
}
if (screenPosition >= two.top) {
$(".teavanamenu li a").removeClass("active");
$(".teavanamenu li a.menu2").addClass("active");
}
if (screenPosition >= three.top) {
$(".teavanamenu li a").removeClass("active");
$(".teavanamenu li a.menu3").addClass("active");
}
if (screenPosition >= four.top) {
$(".teavanamenu li a").removeClass("active");
$(".teavanamenu li a.menu4").addClass("active");
}
});
$(window).scroll();
$(".one, .two").click(function () { $(window).scroll(); });
});
I'm not sure why it can't read the property top. Any help?