Slicknav mobie menu, doesnt work with onClick functions

1k views Asked by At

I'm designing a RPG site for a table top game.

Im a new developer, and the site is mostly finished. I just need a mobile menu.

I have Slicknav up and running for the mobile screen size portion of the site.

The menu is a Nested unordered list structure, with many onclick events. Unlike most menu's with are link tags to products, my menu only has a handful of link's for different parts of the website. The menu items are mostly text, that respond to click events which then fadesIN divs with text to a display window.

The Links to the different parts of the site work fine, I suppose that is expected as thats what SLicknav is really designed for. But the Click events on the link items are ignored.

Here is a sample of the code works normally

$(window).load(function() {
var status = 1;

function statusCheck(){
if (status == 2){

    $("#nol").fadeIn(1000).appendTo("#display_box");
    $("#nol").removeClass("hide");
    $("#display_box").animate({scrollTop:0},500);
    } else{}
//// lots more if statements (much later I figured out eq() would have been 
//// better for this instead of having 20 ID's that do the same thing

$("#nol_menu").on('click', function(){
status = 2;
clearBox();
statusCheck();
// many more on click events that append things.

This code works fine, but when I shrink the screen size and the SlickNav menu pops up on click events no longer function, which kinda defeats the purpose of having a menu.

So again im new to development, but my best guess is that slickNav jscript just takes the text found in the unordered list structure and spits them out in the menu, but this of course removes the ID's and stuff attached to the original text nodes.

But perhaps someone with more expertise can shed some light on this.

I am including the entire Slicknav script file as well.

/*!
 * SlickNav Responsive Mobile Menu v1.0.7
 * (c) 2016 Josh Cope
 * licensed under MIT
 */
;(function ($, document, window) {
var
// default settings object.
    defaults = {
        label: 'MENU',
        duplicate: true,
        duration: 200,
        easingOpen: 'swing',
        easingClose: 'swing',
        closedSymbol: '►',
        openedSymbol: '▼',
        prependTo: 'body',
        appendTo: '',
        parentTag: 'a',
        closeOnClick: false,
        allowParentLinks: false,
        nestedParentLinks: true,
        showChildren: false,
        removeIds: true,
        removeClasses: false,
        removeStyles: false,
        brand: '',
        init: function () {},
        beforeOpen: function () {},
        beforeClose: function () {},
        afterOpen: function () {},
        afterClose: function () {}
    },
    mobileMenu = 'slicknav',
    prefix = 'slicknav';

function Plugin(element, options) {
    this.element = element;

    // jQuery has an extend method which merges the contents of two or
    // more objects, storing the result in the first object. The first object
    // is generally empty as we don't want to alter the default options for
    // future instances of the plugin
    this.settings = $.extend({}, defaults, options);

    // Don't remove IDs by default if duplicate is false
    if (!this.settings.duplicate && !options.hasOwnProperty("removeIds")) {
      this.settings.removeIds = false;
    }

    this._defaults = defaults;
    this._name = mobileMenu;

    this.init();
}

Plugin.prototype.init = function () {
    var $this = this,
        menu = $(this.element),
        settings = this.settings,
        iconClass,
        menuBar;

    // clone menu if needed
    if (settings.duplicate) {
        $this.mobileNav = menu.clone();
    } else {
        $this.mobileNav = menu;
    }

    // remove IDs if set
    if (settings.removeIds) {
      $this.mobileNav.removeAttr('id');
      $this.mobileNav.find('*').each(function (i, e) {
          $(e).removeAttr('id');
      });
    }

    // remove classes if set
    if (settings.removeClasses) {
        $this.mobileNav.removeAttr('class');
        $this.mobileNav.find('*').each(function (i, e) {
            $(e).removeAttr('class');
        });
    }

    // remove styles if set
    if (settings.removeStyles) {
        $this.mobileNav.removeAttr('style');
        $this.mobileNav.find('*').each(function (i, e) {
            $(e).removeAttr('style');
        });
    }

    // styling class for the button
    iconClass = prefix + '_icon';

    if (settings.label === '') {
        iconClass += ' ' + prefix + '_no-text';
    }

    if (settings.parentTag == 'a') {
        settings.parentTag = 'a href="#"';
    }

    // create menu bar
    $this.mobileNav.attr('class', prefix + '_nav');
    menuBar = $('<div class="' + prefix + '_menu"></div>');
    if (settings.brand !== '') {
        var brand = $('<div class="' + prefix + '_brand">'+settings.brand+'</div>');
        $(menuBar).append(brand);
    }
    $this.btn = $(
        ['<' + settings.parentTag + ' aria-haspopup="true" tabindex="0" class="' + prefix + '_btn ' + prefix + '_collapsed">',
            '<span class="' + prefix + '_menutxt">' + settings.label + '</span>',
            '<span class="' + iconClass + '">',
                '<span class="' + prefix + '_icon-bar"></span>',
                '<span class="' + prefix + '_icon-bar"></span>',
                '<span class="' + prefix + '_icon-bar"></span>',
            '</span>',
        '</' + settings.parentTag + '>'
        ].join('')
    );
    $(menuBar).append($this.btn);
    if(settings.appendTo !== '') {
        $(settings.appendTo).append(menuBar);
    } else {
        $(settings.prependTo).prepend(menuBar);
    }
    menuBar.append($this.mobileNav);

    // iterate over structure adding additional structure
    var items = $this.mobileNav.find('li');
    $(items).each(function () {
        var item = $(this),
            data = {};
        data.children = item.children('ul').attr('role', 'menu');
        item.data('menu', data);

        // if a list item has a nested menu
        if (data.children.length > 0) {

            // select all text before the child menu
            // check for anchors

            var a = item.contents(),
                containsAnchor = false,
                nodes = [];

            $(a).each(function () {
                if (!$(this).is('ul')) {
                    nodes.push(this);
                } else {
                    return false;
                }

                if($(this).is("a")) {
                    containsAnchor = true;
                }
            });

            var wrapElement = $(
                '<' + settings.parentTag + ' role="menuitem" aria-haspopup="true" tabindex="-1" class="' + prefix + '_item"/>'
            );

            // wrap item text with tag and add classes unless we are separating parent links
            if ((!settings.allowParentLinks || settings.nestedParentLinks) || !containsAnchor) {
                var $wrap = $(nodes).wrapAll(wrapElement).parent();
                $wrap.addClass(prefix+'_row');
            } else
                $(nodes).wrapAll('<span class="'+prefix+'_parent-link '+prefix+'_row"/>').parent();

            if (!settings.showChildren) {
                item.addClass(prefix+'_collapsed');
            } else {
                item.addClass(prefix+'_open');
            }

            item.addClass(prefix+'_parent');

            // create parent arrow. wrap with link if parent links and separating
            var arrowElement = $('<span class="'+prefix+'_arrow">'+(settings.showChildren?settings.openedSymbol:settings.closedSymbol)+'</span>');

            if (settings.allowParentLinks && !settings.nestedParentLinks && containsAnchor)
                arrowElement = arrowElement.wrap(wrapElement).parent();

            //append arrow
            $(nodes).last().after(arrowElement);


        } else if ( item.children().length === 0) {
             item.addClass(prefix+'_txtnode');
        }

        // accessibility for links
        item.children('a').attr('role', 'menuitem').click(function(event){
            //Ensure that it's not a parent
            if (settings.closeOnClick && !$(event.target).parent().closest('li').hasClass(prefix+'_parent')) {
                    //Emulate menu close if set
                    $($this.btn).click();
                }
        });

        //also close on click if parent links are set
        if (settings.closeOnClick && settings.allowParentLinks) {
            item.children('a').children('a').click(function (event) {
                //Emulate menu close
                $($this.btn).click();
            });

            item.find('.'+prefix+'_parent-link a:not(.'+prefix+'_item)').click(function(event){
                //Emulate menu close
                    $($this.btn).click();
            });
        }
    });

    // structure is in place, now hide appropriate items
    $(items).each(function () {
        var data = $(this).data('menu');
        if (!settings.showChildren){
            $this._visibilityToggle(data.children, null, false, null, true);
        }
    });

    // finally toggle entire menu
    $this._visibilityToggle($this.mobileNav, null, false, 'init', true);

    // accessibility for menu button
    $this.mobileNav.attr('role','menu');

    // outline prevention when using mouse
    $(document).mousedown(function(){
        $this._outlines(false);
    });

    $(document).keyup(function(){
        $this._outlines(true);
    });

    // menu button click
    $($this.btn).click(function (e) {
        e.preventDefault();
        $this._menuToggle();
    });

    // click on menu parent
    $this.mobileNav.on('click', '.' + prefix + '_item', function (e) {
        e.preventDefault();
        $this._itemClick($(this));
    });

    // check for enter key on menu button and menu parents
    $($this.btn).keydown(function (e) {
        var ev = e || event;
        if(ev.keyCode == 13) {
            e.preventDefault();
            $this._menuToggle();
        }
    });

    $this.mobileNav.on('keydown', '.'+prefix+'_item', function(e) {
        var ev = e || event;
        if(ev.keyCode == 13) {
            e.preventDefault();
            $this._itemClick($(e.target));
        }
    });

    // allow links clickable within parent tags if set
    if (settings.allowParentLinks && settings.nestedParentLinks) {
        $('.'+prefix+'_item a').click(function(e){
                e.stopImmediatePropagation();
        });
    }
};

//toggle menu
Plugin.prototype._menuToggle = function (el) {
    var $this = this;
    var btn = $this.btn;
    var mobileNav = $this.mobileNav;

    if (btn.hasClass(prefix+'_collapsed')) {
        btn.removeClass(prefix+'_collapsed');
        btn.addClass(prefix+'_open');
    } else {
        btn.removeClass(prefix+'_open');
        btn.addClass(prefix+'_collapsed');
    }
    btn.addClass(prefix+'_animating');
    $this._visibilityToggle(mobileNav, btn.parent(), true, btn);
};

// toggle clicked items
Plugin.prototype._itemClick = function (el) {
    var $this = this;
    var settings = $this.settings;
    var data = el.data('menu');
    if (!data) {
        data = {};
        data.arrow = el.children('.'+prefix+'_arrow');
        data.ul = el.next('ul');
        data.parent = el.parent();
        //Separated parent link structure
        if (data.parent.hasClass(prefix+'_parent-link')) {
            data.parent = el.parent().parent();
            data.ul = el.parent().next('ul');
        }
        el.data('menu', data);
    }
    if (data.parent.hasClass(prefix+'_collapsed')) {
        data.arrow.html(settings.openedSymbol);
        data.parent.removeClass(prefix+'_collapsed');
        data.parent.addClass(prefix+'_open');
        data.parent.addClass(prefix+'_animating');
        $this._visibilityToggle(data.ul, data.parent, true, el);
    } else {
        data.arrow.html(settings.closedSymbol);
        data.parent.addClass(prefix+'_collapsed');
        data.parent.removeClass(prefix+'_open');
        data.parent.addClass(prefix+'_animating');
        $this._visibilityToggle(data.ul, data.parent, true, el);
    }
};

// toggle actual visibility and accessibility tags
Plugin.prototype._visibilityToggle = function(el, parent, animate, trigger, init) {
    var $this = this;
    var settings = $this.settings;
    var items = $this._getActionItems(el);
    var duration = 0;
    if (animate) {
        duration = settings.duration;
    }

    if (el.hasClass(prefix+'_hidden')) {
        el.removeClass(prefix+'_hidden');
         //Fire beforeOpen callback
            if (!init) {
                settings.beforeOpen(trigger);
            }
        el.slideDown(duration, settings.easingOpen, function(){

            $(trigger).removeClass(prefix+'_animating');
            $(parent).removeClass(prefix+'_animating');

            //Fire afterOpen callback
            if (!init) {
                settings.afterOpen(trigger);
            }
        });
        el.attr('aria-hidden','false');
        items.attr('tabindex', '0');
        $this._setVisAttr(el, false);
    } else {
        el.addClass(prefix+'_hidden');

        //Fire init or beforeClose callback
        if (!init){
            settings.beforeClose(trigger);
        }

        el.slideUp(duration, this.settings.easingClose, function() {
            el.attr('aria-hidden','true');
            items.attr('tabindex', '-1');
            $this._setVisAttr(el, true);
            el.hide(); //jQuery 1.7 bug fix

            $(trigger).removeClass(prefix+'_animating');
            $(parent).removeClass(prefix+'_animating');

            //Fire init or afterClose callback
            if (!init){
                settings.afterClose(trigger);
            } else if (trigger == 'init'){
                settings.init();
            }
        });
    }
};

// set attributes of element and children based on visibility
Plugin.prototype._setVisAttr = function(el, hidden) {
    var $this = this;

    // select all parents that aren't hidden
    var nonHidden = el.children('li').children('ul').not('.'+prefix+'_hidden');

    // iterate over all items setting appropriate tags
    if (!hidden) {
        nonHidden.each(function(){
            var ul = $(this);
            ul.attr('aria-hidden','false');
            var items = $this._getActionItems(ul);
            items.attr('tabindex', '0');
            $this._setVisAttr(ul, hidden);
        });
    } else {
        nonHidden.each(function(){
            var ul = $(this);
            ul.attr('aria-hidden','true');
            var items = $this._getActionItems(ul);
            items.attr('tabindex', '-1');
            $this._setVisAttr(ul, hidden);
        });
    }
};

// get all 1st level items that are clickable
Plugin.prototype._getActionItems = function(el) {
    var data = el.data("menu");
    if (!data) {
        data = {};
        var items = el.children('li');
        var anchors = items.find('a');
        data.links = anchors.add(items.find('.'+prefix+'_item'));
        el.data('menu', data);
    }
    return data.links;
};

Plugin.prototype._outlines = function(state) {
    if (!state) {
        $('.'+prefix+'_item, .'+prefix+'_btn').css('outline','none');
    } else {
        $('.'+prefix+'_item, .'+prefix+'_btn').css('outline','');
    }
};

Plugin.prototype.toggle = function(){
    var $this = this;
    $this._menuToggle();
};

Plugin.prototype.open = function(){
    var $this = this;
    if ($this.btn.hasClass(prefix+'_collapsed')) {
        $this._menuToggle();
    }
};

Plugin.prototype.close = function(){
    var $this = this;
    if ($this.btn.hasClass(prefix+'_open')) {
        $this._menuToggle();
    }
};

$.fn[mobileMenu] = function ( options ) {
    var args = arguments;

    // Is the first parameter an object (options), or was omitted, instantiate a new instance
    if (options === undefined || typeof options === 'object') {
        return this.each(function () {

            // Only allow the plugin to be instantiated once due to methods
            if (!$.data(this, 'plugin_' + mobileMenu)) {

                // if it has no instance, create a new one, pass options to our plugin constructor,
                // and store the plugin instance in the elements jQuery data object.
                $.data(this, 'plugin_' + mobileMenu, new Plugin( this, options ));
            }
        });

    // If is a string and doesn't start with an underscore or 'init' function, treat this as a call to a public method.
    } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {

        // Cache the method call to make it possible to return a value
        var returns;

        this.each(function () {
            var instance = $.data(this, 'plugin_' + mobileMenu);

            // Tests that there's already a plugin-instance and checks that the requested public method exists
            if (instance instanceof Plugin && typeof instance[options] === 'function') {

                // Call the method of our plugin instance, and pass it the supplied arguments.
                returns = instance[options].apply( instance, Array.prototype.slice.call( args, 1 ) );
            }
        });

        // If the earlier cached method gives a value back return the value, otherwise return this to preserve chainability.
        return returns !== undefined ? returns : this;
    }
};
}(jQuery, document, window));
0

There are 0 answers