2
\$\begingroup\$

Other people will be looking at this jquery code and i'm not an expert with jQuery so im asking. How can i make this jQuery code shorter, more efficient, and easier to read?

/* 
HEADER NAVIGATION 
*/
/* navigation tabs click highlight */
$(".header-main-tabs").click(function() {
$(".header-main-tabs").removeClass("header-tab-selected");
$(this).addClass("header-tab-selected");
});
/* drop menu hide and show for desktop */
$(".header-main-tabs").hover(function() {
$(this).children(".header-drop-menu").toggleClass("show-drop-menu-hover");
});

/* search input hide and show when search icon is pressed */
$("#search-icon-container span").click(function() {
$(this).toggleClass("fa-times");
$("#search-input-container").fadeToggle("fast");
});

/* mobile navigation */
/* show mobile tabs when toggle nav mobile button is clicked and when browser width is      over 990px */
/* toggle mobile navigation when nav button is clicked */
$("#toggle-mobile-nav").click(function() {
$("#nav-tabs-list").slideToggle();
$("#toggle-mobile-nav").toggleClass("toggle-mobile-nav-clicked");
});

var browserWidth = $(window).width();

$(window).resize(function() {
browserWidth = $(window).width();
if (browserWidth > 990) {
    $("#nav-tabs-list").show();
}
 });

$('#nav-tabs-list li').click(function() {
if (browserWidth <= 990) {
    $('#nav-tabs-list').slideUp();
}
});

/* always show drop menu when on mobile version (when browser width is below 960px) */
$(document).ready(function() { 
fadeMobile();  
});

$(window).resize(function() { 
  fadeMobile();  
});

function fadeMobile() {
  browserWidth = $(window).width(); 
  if (browserWidth < 990) { 
$("#nav-tabs-list").hide();
    $('.header-drop-menu').show(); 
    $("#toggle-mobile-nav").removeClass("toggle-mobile-nav-clicked");
      }
    }
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

Easier to Read

Make sure you use the same standard everywhere, indent nicely and be consistent. Then the code already looks a lot better:

/* 
HEADER NAVIGATION 
*/

/* navigation tabs click highlight */
$(".header-main-tabs").click(function() {
    $(".header-main-tabs").removeClass("header-tab-selected");
    $(this).addClass("header-tab-selected");
});

/* drop menu hide and show for desktop */
$(".header-main-tabs").hover(function() {
    $(this).children(".header-drop-menu").toggleClass("show-drop-menu-hover");
});

/* search input hide and show when search icon is pressed */
$("#search-icon-container span").click(function() {
    $(this).toggleClass("fa-times");
    $("#search-input-container").fadeToggle("fast");
});

/* mobile navigation */
/* show mobile tabs when toggle nav mobile button is clicked and when browser width is      over 990px */
/* toggle mobile navigation when nav button is clicked */
$("#toggle-mobile-nav").click(function() {
    $("#nav-tabs-list").slideToggle();
    $("#toggle-mobile-nav").toggleClass("toggle-mobile-nav-clicked");
});

var browserWidth = $(window).width();

$(window).resize(function() {
    browserWidth = $(window).width();
    if (browserWidth > 990) {
        $("#nav-tabs-list").show();
    }
});

$('#nav-tabs-list li').click(function() {
    if (browserWidth <= 990) {
        $('#nav-tabs-list').slideUp();
    }
});

/* always show drop menu when on mobile version (when browser width is below 960px) */
$(document).ready(function() { 
    fadeMobile();  
});

$(window).resize(function() { 
    fadeMobile();  
});

function fadeMobile() {
    browserWidth = $(window).width(); 
    if (browserWidth < 990) { 
        $("#nav-tabs-list").hide();
        $('.header-drop-menu').show(); 
        $("#toggle-mobile-nav").removeClass("toggle-mobile-nav-clicked");
    }
}

Shorter

  • Don't use $('...') twice for the same selector and don't use it in an event. Instead, save the element in a variable and use that. It's both faster and easier to read
  • Group events on the same element. The first 2 events are both placed on $('.header-main-tabs'), group them together
  • Put all code in the $(document).ready() callback, this will avoid "DOM not ready" errors
  • Use CSS to hide/show elements, don't use jQuery for that
  • Combine the same events, you now have 2 $(window).resize() events
  • Don't show on each resize, only show if it was hidden before
  • Don't use global variables to store data
/* 
HEADER NAVIGATION 
*/

$(document).ready(function () {

    var $mainTabs = $(".header-main-tabs");
    var $searchInputContainer = $("#search-input-container");

    $mainTabs
        .click(function() { // navigation tabs click highlight
            $mainTabs.removeClass("header-tab-selected");

            $(this).addClass("header-tab-selected");
        }) 
        .hover(function() { // drop menu hide and show for desktop
            $(this).children(".header-drop-menu").toggleClass("show-drop-menu-hover");
        });

    /* search input hide and show when search icon is pressed */
    $("#search-icon-container span").click(function() {
        $(this).toggleClass("fa-times");

        $searchInputContainer.fadeToggle("fast");
    });

    /* mobile navigation */
    /* show mobile tabs when toggle nav mobile button is clicked and when browser width is      over 990px */
    /* toggle mobile navigation when nav button is clicked */
    var $navTabsList = $("#nav-tabs-list");
    var $toggleMobileNav = $("#toggle-mobile-nav");
    var $headerDropMenu = $('.header-drop-menu');

    $toggleMobileNav.click(function() {
        $navTabsList.slideToggle();

        $toggleMobileNav.toggleClass("toggle-mobile-nav-clicked");
    });

    $(window).resize(function() {
        browserWidth = $(window).width();

        if (browserWidth > 990 && $navTabsList.is(':hidden')) {
            $navTabsList.show();
        }

        fadeMobile();
    });

    $('li', $navTabsList).click(function() {
        if ($(window).width() <= 990) {
            $navTabsList.slideUp();
        }
    });

    /* always show drop menu when on mobile version (when browser width is below 960px) */
    fadeMobile();  

    function fadeMobile(browserWidth) {
        if (browserWidth < 990) { 
            if ($navTabsList.is(':visible')) {
                $navTabsList.hide();
            }

            if ($headerDropMenu.is(':hidden')) {
                $headerDropMenu.show(); 
            }

            if ($toggleMobileNav.hasClass('toggle-mobile-nav-clicked')) {
                $toggleMobileNav.removeClass("toggle-mobile-nav-clicked");
            }
        }
    }

});
\$\endgroup\$
2
\$\begingroup\$

All changes are documented below, however here is a quick-hit list:

  • contain everything in $(document).ready() to ensure elements exist before assigning handlers
  • make better use of caching, .find(), and event delegation
  • combine width-specific functions into a single item, with width passed as parameter
  • make use of .on() rather than shorthand (.click(), .hover()) to combine bindings into a single handler
  • split hover into mouseenter and mouseleave to prevent possible double-toggling

Many of these not only make the code more readable, but makes runtime much faster by preventing excessive DOM querying, having smaller object sets to source from, consolidating similarly-applied code, and removal of unneeded code / event handlers.

Here's a draft of the new setup:

/* contain everything within a $(document).ready() function to ensure elements 
* exist before handlers assigned to them - shorthand of $(function used here */

$(function() {
    /* 
    * cache objects used more than once, as well as appropriate children
    * make use of $(this) rather than re-querying the DOM
    * make use of .find() selector when caching children rather than requerying for a new object
    */
    var $mainTabs = $(".header-main-tabs"),
        $headerDropMenu = $mainTabs.children('.header-drop-menu'),
        $navTabsList = $("#nav-tabs-list"),
        $searchInputContainer = $("#search-input-container"),
        $toggleMobileNav = $("#toggle-mobile-nav");

    /* place function declarations prior to event handlers in case any of the handlers use it,
    * and consolidate your winWidth-based logic into one function */
    function setMobile(winWidth) {      
        /* this was two separate functions, since you run this every time you can consolidate */
        if (winWidth <= 990) { 
            $navTabsList.hide();
            $headerDropMenu.show(); 
            $toggleMobileNav.removeClass("toggle-mobile-nav-clicked");

            /* only bind the click handler if winWidth calls for it, and use delegation of click 
            * events where possible, rather than using CSS selector */
            $navTabsList.on('click','li',function() {           
                $navTabsList.slideUp();
            });
        } else {
            $navTabsList.show();
            $headerDropMenu.hide(); 
            $toggleMobileNav.addClass("toggle-mobile-nav-clicked");

            /* else remove the binding */
            $navTabsList.off('click');
        }
    }

    /* combine multiple on declarations into a single .on() binding */
    $mainTabs.on({
        click:function(){
            $mainTabs.removeClass("header-tab-selected");
            $(this).addClass("header-tab-selected");
        },
        /* separate hover event into mouseenter / mouseleave to prevent possible double-toggling,
        * which can happen on rapid hover changes and .toggleClass() */
        mouseenter:function(){
            $headerDropMenu.addClass("show-drop-menu-hover");
        },
        mouseleave:function(){
            $headerDropMenu.removeClass("show-drop-menu-hover");
        }
    });

    $searchInputContainer.on('click','span',function() {
        $(this).toggleClass("fa-times");
            $searchInputContainer.fadeToggle("fast");
    });

    $toggleMobileNav.on('click',function() {
        $navTabsList.slideToggle();
        $(this).toggleClass("toggle-mobile-nav-clicked");
    });

    /* call your consolidated function, passing the width as parameter rather then
    * assigning to a variable within the function */
    setMobile($(window).width()); 

    /* change to non-shorthand in case you wanted to add a different window 
    * handler later ('load' to wait for images, for example). also, contain it within the 
    * document.ready so that it doesnt accidentally fire before document.ready (bug in early 
    * webkit versions) */
    $(window).on('resize',function() {
        setMobile($(window).width());
    });
});

Here it is without the clutter of the comments:

$(function() {
    var $mainTabs = $(".header-main-tabs"),
        $headerDropMenu = $mainTabs.children('.header-drop-menu'),
        $navTabsList = $("#nav-tabs-list"),
        $searchInputContainer = $("#search-input-container"),
        $toggleMobileNav = $("#toggle-mobile-nav");

    function setMobile(winWidth) {      
        if (winWidth <= 990) { 
            $navTabsList.hide();
            $headerDropMenu.show(); 
            $toggleMobileNav.removeClass("toggle-mobile-nav-clicked");

            $navTabsList.on('click','li',function() {           
                $navTabsList.slideUp();
            });
        } else {
            $navTabsList.show();
            $headerDropMenu.hide(); 
            $toggleMobileNav.addClass("toggle-mobile-nav-clicked");

            $navTabsList.off('click');
        }
    }

    $mainTabs.on({
        click:function(){
            $mainTabs.removeClass("header-tab-selected");
            $(this).addClass("header-tab-selected");
        },
        mouseenter:function(){
            $headerDropMenu.addClass("show-drop-menu-hover");
        },
        mouseleave:function(){
            $headerDropMenu.removeClass("show-drop-menu-hover");
        }
    });

    $searchInputContainer.on('click','span',function() {
        $(this).toggleClass("fa-times");
            $searchInputContainer.fadeToggle("fast");
    });

    $toggleMobileNav.on('click',function() {
        $navTabsList.slideToggle();
        $(this).toggleClass("toggle-mobile-nav-clicked");
    });

    setMobile($(window).width()); 

    $(window).on('resize',function() {
        setMobile($(window).width());
    });
});
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.