You can see the JavaScript at the end of this post in action here.
I've recently picked up JavaScript/jQuery and would love some feedback on how I'm implementing this functionality. As you can see, the li
items in the navbar change based on where the user's scroll position is.
Is this an efficient way of implementing this? Am I using best practice?
I've already learned from posting a similar question on Reddit to cache objects in variables, that jQuery reads selectors from right to left, and that I should use preventDefault()
instead of return false
. Is there anything else I should do to improve my JavaScript/jQuery skills?
jQuery(function ($) {
"use strict"; //don't know what this means but JSHint tells me to put this here...
var $decTops = [], $decHeights = [], mobileView = 780, $i = 0, $nav = $("#navigation"), $decadeDivs = $("div.decade"), $timeline = $("#timeline"); // caching various elements in var names
function coordsUpdate() { // updates where each decade starts and how long the decade's div is everytime the user scrolls/resizes the window and pushes those values to two arrays
$decTops.length = 0, $decHeights.length = 0;
$decadeDivs.each(function() {
$decTops.push($(this).offset().top-51);
$decHeights.push($(this).outerHeight(true));
});
$decTops.push($("#footer-wrapper").offset().top-21); // bugfix: push the footer's position to the array as well to avoid the disappearing navbar bug.
}
function decadeCounter() { // counts which decade the user is on based on scrollTop position
var $currentLoc = $(window).scrollTop();
while ($currentLoc > $decTops[$i] + $decHeights[$i]) {
$i++;
}
while ($currentLoc > $decTops[0] && $currentLoc < $decTops[$i]) {
$i--;
}
navAdjust($i, $currentLoc);
//console.log('currentHeight: ' + $(window).scrollTop() + ', decTops: ' + $decTops + ', decHeights: ' + $decHeights);
//console.log('$i: ' + $i + ', $old: ' + $old);
}
function navAdjust($i, $currentLoc) { //fix the navbar to the top and highlight the correct decade in the navbar or fix the decade header to top if using mobile viewport.
if ($(window).width() >= mobileView) {
$timeline.find("div.top-fix").removeClass("top-fix"); //don't want the decade headers on top if using desktop viewport
if ($currentLoc >= $decTops[0] && $i <= 6) {
$nav.addClass("fixed-nav").removeClass("nav-width-fix");
$nav.find("a.active").removeClass("active");
$nav.find("a:eq(" + $i + ")").addClass("active");
}
else { //unfix navbar if user is on top of page or at bottom
$nav.removeClass("fixed-nav").addClass("nav-width-fix");
$nav.find("a.active").removeClass("active");
}
}
else {
if ($currentLoc >= $decTops[0]) { //don't want the navbar on top if using mobile viewport
$timeline.find(".top-fix").removeClass("top-fix").removeClass("top-fix");
$timeline.find(".decade:eq(" + $i + ") .dec-banner").addClass("top-fix");
}
else {
$timeline.find("div.top-fix").removeClass("top-fix");
}
}
}
$nav.on("click", "a", function() { // bring user to specific decade if clicked on in the main navbar
$nav.find("a.active").removeClass("active");
var $navlink = $(this).attr("href");
$('html,body').stop().animate({scrollTop: $($navlink).offset().top},"fast");
return false;
});
$timeline.find(".dec-banner").on("click", "a", function(event) { // bring user to previous or next decade if clicked on in the decade header
var $navlink = $(this).attr("href");
if ($(this).parent().hasClass("decade-jump-prev")) {
$("html,body").stop().animate({scrollTop: $($navlink).offset().top-41},"fast");
}
if ($(this).parent().hasClass("decade-jump-next")) {
$("html,body").stop().animate({scrollTop: $($navlink).offset().top+10},"fast");
}
event.preventDefault();
});
// add listener
$(window).on('load orientationchange resize', coordsUpdate);
$(window).on('scroll load orientationchange resize', decadeCounter);
});