3
\$\begingroup\$

I'm fairly new to JS/jQuery (a few months), and I think it's time to start getting involved in the community. So I wrote a little plugin. Nothing revolutionary. Really, the project is to write a clean, workable plugin. Any and all thoughts and suggestions on how I can make the code cleaner, or the animations smoother, or anything, are very much appreciated.

Here's the plugin in action: http://jsfiddle.net/VA7P5/

Here is the plugin code:

(function ($) {

$.fn.menuBar = function (options) { 

    var defaults = {

        width: 145, // Width of Sidebar
        left: true, // If true, sidebar is positioned left. If false, it's positioned right
        height: 80, // Height of footer
        barColor: '#000', // Color of three-bar menu before it's opened
        menuBackground: '#303030', // Background color of sidebar and footer
        closeColor: '#fff' // Color of close-button
    };

    var options = $.extend(defaults, options);

    return this.each(function () {

        var i = $(this);
        var o = options;
        var width = $('nav.sidebar').css('width');
        var height = $('footer.hidden').css('height');
        var closeColor = $('.bar').css('background');
        var barColor = $('.bar').css('background');
        var barOne = $('.menu-bar-top');
        var barTwo = $('.menu-bar-bottom');
        var barThree = $('.menu-bar-mid');
        var menuTrigger = $('nav.sidebar a');
        var fadeWrapper = $('#fade-wrapper');
        var nav = $('nav.sidebar');
        var footerHidden  = $('footer.hidden');
        var bar = $('.bar');

        bar.css('background', o.barColor);

        if (o.left) {
            nav.css({
                'width': o.width,
                'left': o.width - (o.width * 2),
                'background': o.menuBackground
            });
            $('.menu-trigger').css({
                'left': 0
            });
        } else {
            nav.css({
                'width': o.width,
                'right': o.width - (o.width * 2),
                'background': o.menuBackground
            });
            $('.menu-trigger').css({
                'right': 0
            });
        }

        footerHidden.css({
            'height': o.height,
            'bottom': o.height - (o.height * 2), 
            'background': o.menuBackground
        });

        i.click(function(){
            if (i.hasClass('open')) {

                closeMenu();
                i.removeClass('open');

                // Allow scrolling again when menu is closed
                $('body').css('overflow', '');

            } else {

                openMenu();
                i.addClass('open');

                // No scrolling while menu is open
                $('body').css('overflow', 'hidden');

            }
        });

        $('#fade-wrapper').click(function(){
            closeMenu();
            i.removeClass('open');
            $('body').css('overflow', '');
        });


/*===========================================================================================================
                                      Opening/Closing Functions
===========================================================================================================*/

        function openMenu() {
            fadeWrapper.fadeIn(100, function(){
                barOne.css({
                    'top': '8px',
                    'transform': 'rotate(405deg)',
                    '-webkit-transform': 'rotate(405deg)',
                    '-moz-transform': 'rotate(405deg)',
                    '-ms-transform': 'rotate(405deg)',
                    '-o-transform': 'rotate(405deg)'
                });

                barTwo.css({
                    'top': '8px',
                    'transform': 'rotate(-405deg)',
                    '-webkit-transform': 'rotate(-405deg)',
                    '-moz-transform': 'rotate(-405deg)',
                    '-ms-transform': 'rotate(-405deg)',
                    '-o-transform': 'rotate(-405deg)'
                });
                if (o.left) {
                    nav.animate({'left': '+=' + o.width}, 200);
                } else {
                    nav.animate({'right': '+=' + o.width}, 200);
                }
                footerHidden.animate({'bottom': '+=' + o.height}, 200);
                barThree.fadeOut(100);
                bar.css('background', o.closeColor);
            });
        }

        function closeMenu() {
            setTimeout(function(){
                barThree.fadeTo(100, 1);
                fadeWrapper.fadeOut(100);
                if (o.left) {
                    nav.animate({'left': '-=' + o.width}, 200);
                } else {
                    nav.animate({'right': '-=' + o.width}, 200);
                }
                footerHidden.animate({'bottom': '-=' + o.height}, 200);
                bar.css('background', o.barColor);

                barOne.css({
                    'top': '3px',
                    'transform': 'rotate(360deg)',
                    '-webkit-transform': 'rotate(360deg)',
                    '-moz-transform': 'rotate(360deg)',
                    'ms-transform': 'rotate(360deg)',
                    'o-transform': 'rotate(360deg)'
                });

                barTwo.css({
                    'top': '13px',
                    'transform': 'rotate(-360deg)',
                    '-webkit-transform': 'rotate(-360deg)',
                    '-moz-transform': 'rotate(-360deg)',
                    '-ms-transform': 'rotate(-360deg)',
                    '-o-transform': 'rotate(-360deg)'
                });}, 1);
        }
    });
};
})(jQuery);

The necessary HTML:

<nav class="sidebar">
        <a class="menu cursor" title="Menu">
            <div class="menu-trigger">
                <div class="bar-container">
                    <div class="bar menu-bar-top"></div>
                    <div class="bar menu-bar-mid"></div>
                    <div class="bar menu-bar-bottom"></div>
                </div>
            </div>
        </a>
        <!-- Sidebar content goes here -->
    </nav>

    <div id="fade-wrapper"></div>

    <footer class="hidden">
        <!-- Footer content goes here -->
    </footer>

The necessary CSS:

a.cursor {
    cursor: pointer;
}

#fade-wrapper {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    background: rgba(0, 0, 0, 0.3);
    z-index: 5000;
}

nav.sidebar {
    position: fixed;
    top: 0;
    height: 100%;
    z-index: 9999;
}

.menu-trigger {
    position: fixed;
    top: 8px;
    width: 40px;
    height: 20px;
    line-height: 40px;
}

.bar-container {
    margin-top: 3px;
    height: 13px;
}

.bar {
    position: absolute;
    height: 3px;
    width: 90%;
    outline: 1px solid transparent;
    -webkit-transition: all .5s ease;
    -moz-transition: all .5s ease;
    -ms-transition: all .5s ease;
    -o-transition: all .5s ease;
    transition: all .5s ease;
}

.menu-bar-top {
    top: 3px;
    left: 2px;
}

.menu-bar-mid {
    top: 8px;
    left: 2px;
}

.menu-bar-bottom {
    top: 13px;
    left: 2px;
}

footer.hidden {
    position: fixed;
    left: 0;
    width: 100%;
    z-index: 9999;
}
\$\endgroup\$
1
\$\begingroup\$

I like your code, it is easy to follow, has comments, well named variables etc. The only thing I would point out is that you are repeating yourself here and there. So I will focus on that:

  • This piece of code:

    if (o.left) {
        nav.css({
            'width': o.width,
            'left': o.width - (o.width * 2),
            'background': o.menuBackground
        });
        $('.menu-trigger').css({
            'left': 0
        });
    } else {
        nav.css({
            'width': o.width,
            'right': o.width - (o.width * 2),
            'background': o.menuBackground
        });
        $('.menu-trigger').css({
            'right': 0
        });
    }
    

    Is really repeating the same thing but with left being replaced with right, you could just assign left or right to a variable first.

    var key = o.left ? 'left' : 'right';
     nav.css({
         'width': o.width,
         'background': o.menuBackground
     }).css( key, o.width - (o.width * 2) );
     $('.menu-trigger').css( key , 0 );
    
  • This piece of code is repeated a few times at well, the only different is the degrees and the value of 'top'.

    barTwo.css({
        'top': '13px',
        'transform': 'rotate(-360deg)',
        '-webkit-transform': 'rotate(-360deg)',
        '-moz-transform': 'rotate(-360deg)',
        '-ms-transform': 'rotate(-360deg)',
        '-o-transform': 'rotate(-360deg)'
    });}, 1);
    

    You could consider a helper function that this transformation for you

    function generateTransformation( top , transformation ){
        return {
            'top': top,
            'transform': transformation,
            '-webkit-transform': transformation,
            '-moz-transform': transformation,
            '-ms-transform': transformation,
            '-o-transform': transformation'              
        };
    }
    

    Then, you can simply do

    barOne.css( generateTransformation( '3px'  , 'rotate(360deg)'  ));
    barTwo.css( generateTransformation( '13px' , 'rotate(-360deg)' ));
    
  • These:

    if (o.left) {
        nav.animate({'left': '+=' + o.width}, 200);
    } else {
        nav.animate({'right': '+=' + o.width}, 200);
    }
    

    I will leave to you as an exercise for the reader.

  • var bar = $('.bar'); <- This will select 3 bars, perhaps call it bars ?
  • var closeColor = $('.bar').css('background'); <- This takes the background color of the first bar, I would put that in a comment or make it more obvious by calling $('.bar').eq(0).css('background'); or even better determine bars first and then go for bars.eq(0).css('background');
\$\endgroup\$
  • \$\begingroup\$ Thanks a ton! This kind of feedback is exactly what I'm looking for. I want to post an update, but before I do: Should I A) replace the code in the original post with the new code, B) Add the new code block to the original post or C) post the new code block as an answer? \$\endgroup\$ – MyCompassSpins Apr 21 '14 at 17:49
  • \$\begingroup\$ You are welcome, Option C is the way to go. \$\endgroup\$ – konijn Apr 21 '14 at 18:08
  • \$\begingroup\$ I just posted the update. I've got a quick question, though: Why is it that elem.css(var, var) works, but elem.css({var: var}) does not? \$\endgroup\$ – MyCompassSpins Apr 21 '14 at 18:47
  • \$\begingroup\$ Gah, I meant Option D -> New Question, my bad, I misread your options \$\endgroup\$ – konijn Apr 21 '14 at 18:49
  • 1
    \$\begingroup\$ Last comment, though feel free to ask questions in Chat ( chat.stackexchange.com/rooms/8595/the-2nd-monitor ) by addressing @Konijn, that would really be an SO question, I cant tell off hand what is going on. \$\endgroup\$ – konijn Apr 21 '14 at 19:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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