1

I suspect my question may be a bit obtuse, and I apologize. Here is what I am trying to achieve:

I am using this multi-level push menu, and it works fine out of the box. However, I would like the multi-level menu to close after a modal is submitted and closed. I am having a difficult time figuring out how to trigger this menu to close.

So, on to some code. Up first, the Javascript that runs the menu itself.

mlPushMenu.prototype = {
    defaults : {
        // overlap: there will be a gap between open levels
        // cover: the open levels will be on top of any previous open level
        type : 'overlap', // overlap || cover
        // space between each overlaped level
        levelSpacing : 40,
        // classname for the element (if any) that when clicked closes the current level
        backClass : 'mp-back'
    },
    _init : function() {
        // if menu is open or not
        this.open = false;
        // level depth
        this.level = 0;
        // the moving wrapper
        this.wrapper = document.getElementById( 'mp-pusher' );
        // the mp-level elements
        this.levels = Array.prototype.slice.call( this.el.querySelectorAll( 'div.mp-level' ) );
        // save the depth of each of these mp-level elements
        var self = this;
        this.levels.forEach( function( el, i ) { el.setAttribute( 'data-level', getLevelDepth( el, self.el.id, 'mp-level' ) ); } );
        // the menu items
        this.menuItems = Array.prototype.slice.call( this.el.querySelectorAll( 'li' ) );
        // if type == "cover" these will serve as hooks to move back to the previous level
        this.levelBack = Array.prototype.slice.call( this.el.querySelectorAll( '.' + this.options.backClass ) );
        // event type (if mobile use touch events)
        this.eventtype = mobilecheck() ? 'touchstart' : 'click';
        // add the class mp-overlap or mp-cover to the main element depending on options.type
        classie.add( this.el, 'mp-' + this.options.type );
        // initialize / bind the necessary events
        this._initEvents();
    },
    _initEvents : function() {
        var self = this;

        // the menu should close if clicking somewhere on the body
        var bodyClickFn = function( el ) {
            self._resetMenu();
            el.removeEventListener( self.eventtype, bodyClickFn );
        };

        // open (or close) the menu
        this.trigger.addEventListener( this.eventtype, function( ev ) {
            ev.stopPropagation();
            ev.preventDefault();
            if( self.open ) {
                self._resetMenu();
            }
            else {
                self._openMenu();
                // the menu should close if clicking somewhere on the body (excluding clicks on the menu)
                document.addEventListener( self.eventtype, function( ev ) {
                    if( self.open && !hasParent( ev.target, self.el.id ) ) {
                        bodyClickFn( this );
                    }
                } );
            }
        } );

        // opening a sub level menu
        this.menuItems.forEach( function( el, i ) {
            // check if it has a sub level
            var subLevel = el.querySelector( 'div.mp-level' );
            if( subLevel ) {
                el.querySelector( 'a' ).addEventListener( self.eventtype, function( ev ) {
                    ev.preventDefault();
                    var level = closest( el, 'mp-level' ).getAttribute( 'data-level' );
                    if( self.level <= level ) {
                        ev.stopPropagation();
                        classie.add( closest( el, 'mp-level' ), 'mp-level-overlay' );
                        self._openMenu( subLevel );
                    }
                } );
            }
        } );

        // closing the sub levels :
        // by clicking on the visible part of the level element
        this.levels.forEach( function( el, i ) {
            el.addEventListener( self.eventtype, function( ev ) {
                ev.stopPropagation();
                var level = el.getAttribute( 'data-level' );
                if( self.level > level ) {
                    self.level = level;
                    self._closeMenu();
                }
            } );
        } );

        // by clicking on a specific element
        this.levelBack.forEach( function( el, i ) {
            el.addEventListener( self.eventtype, function( ev ) {
                ev.preventDefault();
                var level = closest( el, 'mp-level' ).getAttribute( 'data-level' );
                if( self.level <= level ) {
                    ev.stopPropagation();
                    self.level = closest( el, 'mp-level' ).getAttribute( 'data-level' ) - 1;
                    self.level === 0 ? self._resetMenu() : self._closeMenu();
                }
            } );
        } );    
    },
    _openMenu : function( subLevel ) {
        // increment level depth
        ++this.level;

        // move the main wrapper
        var levelFactor = ( this.level - 1 ) * this.options.levelSpacing,
            translateVal = this.options.type === 'overlap' ? this.el.offsetWidth + levelFactor : this.el.offsetWidth;

        this._setTransform( 'translate3d(' + translateVal + 'px,0,0)' );

        if( subLevel ) {
            // reset transform for sublevel
            this._setTransform( '', subLevel );
            // need to reset the translate value for the level menus that have the same level depth and are not open
            for( var i = 0, len = this.levels.length; i < len; ++i ) {
                var levelEl = this.levels[i];
                if( levelEl != subLevel && !classie.has( levelEl, 'mp-level-open' ) ) {
                    this._setTransform( 'translate3d(-100%,0,0) translate3d(' + -1*levelFactor + 'px,0,0)', levelEl );
                }
            }
        }
        // add class mp-pushed to main wrapper if opening the first time
        if( this.level === 1 ) {
            classie.add( this.wrapper, 'mp-pushed' );
            this.open = true;
        }
        // add class mp-level-open to the opening level element
        classie.add( subLevel || this.levels[0], 'mp-level-open' );
    },
    // close the menu
    _resetMenu : function() {
        this._setTransform('translate3d(0,0,0)');
        this.level = 0;
        // remove class mp-pushed from main wrapper
        classie.remove( this.wrapper, 'mp-pushed' );
        this._toggleLevels();
        this.open = false;
    },
    // close sub menus
    _closeMenu : function() {
        var translateVal = this.options.type === 'overlap' ? this.el.offsetWidth + ( this.level - 1 ) * this.options.levelSpacing : this.el.offsetWidth;
        this._setTransform( 'translate3d(' + translateVal + 'px,0,0)' );
        this._toggleLevels();
    },
    // translate the el
    _setTransform : function( val, el ) {
        el = el || this.wrapper;
        el.style.WebkitTransform = val;
        el.style.MozTransform = val;
        el.style.transform = val;
    },
    // removes classes mp-level-open from closing levels
    _toggleLevels : function() {
        for( var i = 0, len = this.levels.length; i < len; ++i ) {
            var levelEl = this.levels[i];
            if( levelEl.getAttribute( 'data-level' ) >= this.level + 1 ) {
                classie.remove( levelEl, 'mp-level-open' );
                classie.remove( levelEl, 'mp-level-overlay' );
            }
            else if( Number( levelEl.getAttribute( 'data-level' ) ) == this.level ) {
                classie.remove( levelEl, 'mp-level-overlay' );
            }
        }
    }
}

The method I would like to invoke would be _resetMenu. I have tried this a couple of ways. The primary was trying to trigger the click event of the button that opens the menu:

<a href="#" id="trigger" class="show-root-menu"><i class="fa fa-reorder"></i></a>

However:

$('#trigger').trigger('click');

does not work as expected. I would expect this to be fired:

// open (or close) the menu
        this.trigger.addEventListener( this.eventtype, function( ev ) {
            ev.stopPropagation();
            ev.preventDefault();
            if( self.open ) {
                self._resetMenu();
            }
            else {
                self._openMenu();
                // the menu should close if clicking somewhere on the body (excluding clicks on the menu)
                document.addEventListener( self.eventtype, function( ev ) {
                    if( self.open && !hasParent( ev.target, self.el.id ) ) {
                        bodyClickFn( this );
                    }
                } );
            }
        } );

In this case, this.eventtype is 'click'.

I suspect this may have something to do with how this functionality is coded with an object. While I have decent Javascript skills, I have not had much exposure to using it in this manner.

I am instantiating the menu thusly:

new mlPushMenu(document.getElementById('mp-menu'), document.getElementById('trigger'));

I realize I could extract this method, but that seems to violate some best practices in general.

Any suggestions will be most appreciated!

1 Answer 1

1

You can't use this module as-is to do this. The only event types it supports are "click" and "touchstart" and its mobilecheck() method determines which one of the two it uses.

You'll either have to fake a click on a trigger element or you'll have to modify this module. You can do this with the .click() method of a DOM element. .trigger.click() probably won't do it; you would probably be better off just getting the element (via $('#trigger') or document.getElementById()) and then calling the .click() method of that.

I just tried this in Chrome with an <a> with style display:none and it followed the link so it may work for another hidden element set to trigger. It's hacky but it might be preferable to changing the module.

Sign up to request clarification or add additional context in comments.

1 Comment

Ok. As silly as it seems to me, document.getElementById('trigger').click(); worked. Not sure why the jQuery route failed, but thank you Ryan!

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.