2

I'm doing a responsive menu that check the window.resize event and, when it fits the minimum browser width, a click function for a button is allowed. If the browser width is greater, then the click function is unbound. I need to do this because the same element that is the button on the mobile version, is a visual element on the desktop version.

The problem is that, with the code that I have now, when the page is loaded (desktop or mobile screen), the click function works fine. But, if I resize the browser and click on the element again, it doesn't work. If I want to mobile nav works, I need to refresh the page to make it work. Really annoying.

To help understand what is happening, I've made a simple example. Here's is the simple code (just to check the click function issue)

HTML

<!-- WRAPPER -->
<div id="freakers-wrapper">
    <!-- HEADER -->
    <header id="header">
        <div class="header-bottom">
            <div class="container">
                <div class="row">
                    <div class="col-md-5">
                        <!-- MENU -->
                        <nav class="menu-wrapper-left">
                            <a class="menu-trigger" href="#">
                                <span></span>
                            </a>

                            <ul id="main-menu" class="menu menu--main menu--left main-menu">
                                <li><a href="#">Home</a></li>

                                <li class="has-children">
                                    <a href="#">Shop</a>

                                    <ul class="sub-menu is-hidden">
                                        <li class="go-back"><a href="#">Back</a></li>
                                        <li><a href="#">Shop 1</a></li>
                                        <li><a href="#">Shop 2</a></li>
                                        <li><a href="#">Shop 3</a></li>
                                    </ul>
                                </li>
                                <li><a href="#" >Blog</a></li>
                            </ul>   <!-- end main-menu -->
                        </nav>  <!-- end menu-wrapper -->
                    </div>

                    <div class="col-md-2">
                        <div class="logo">
                            <a href="#">
                                <img src="images/logo.png" alt="Logo" class="img-responsive">
                            </a>
                        </div>
                    </div>

                    <div class="col-md-5">
                        <!-- MENU -->
                        <nav class="menu-wrapper-right">
                            <ul id="main-menu" class="menu menu--main menu--right main-menu">
                                <li><a href="#">help</a></li>
                                <li><a href="#">lookbook</a></li>
                                <li><a href="#">model</a></li>
                            </ul>   <!-- end main-menu -->
                        </nav>  <!-- end menu-wrapper -->
                    </div>
                </div>
            </div>
        </div>  <!-- end header-bottom -->
    </header>   <!-- end header -->

    <!-- MOBILE NAV -->
    <div id="mobile-nav"></div>
</div>  <!-- end freakers-wrapper -->

JS

(function($) {
    "use strict";

    $(document).ready(function () {

        $(window).on('load resize', function(){
            moveNavigation();
        });

        /* ----------------------------------------------------------------------
            Main Menu
        ---------------------------------------------------------------------- */

        //if you change this breakpoint, don't forget to update this value as well
        var MqL = 1030,
            menuLeft = $('.menu-wrapper-left').html(),
            menuRight = $('.menu-wrapper-right').html();
            console.log(menuRight);
            console.log(menuLeft);
        //move nav element position according to window width
        // moveNavigation();

        //mobile - open lateral menu clicking on the menu icon
        $(document).on('click', '.menu-trigger', function(event){
            event.preventDefault();
            if($('#freakers-wrapper').hasClass('push-content')){
                closeNav();
            }else{
                $('#freakers-wrapper').addClass('push-content');
                $('#mobile-nav .menu').addClass('menu--open');
                $(this).addClass('nav-is-visible');
            }
        });

        //open submenu
        $('.has-children').on('click', function(event){
            var selected = $(this);
            if( selected.children('ul').hasClass('is-hidden') ) {
                selected.children('ul').removeClass('is-hidden');
            }
        });

        //submenu items - go back link
        $('.go-back').on('click', function(evt){
            evt.stopPropagation();
            $(this).parent('ul')
                .addClass('is-hidden')
                .parent('.has-children')
                .parent('ul');
        });

        function closeNav(){
            $('#freakers-wrapper').removeClass('push-content');
            $('.menu--main').removeClass('menu--open');
            $('.has-children ul').addClass('is-hidden');
        }

        function checkWindowWidth() {
            //check window width (scrollbar included)
            var e = window, 
                a = 'inner';
            if (!('innerWidth' in window )) {
                a = 'client';
                e = document.documentElement || document.body;
            }
            if ( e[ a+'Width' ] >= MqL ){
                closeNav();

                if ( $('.menu-trigger').hasClass('menu-trigger-open') ){
                    $('.menu-trigger').removeClass('menu-trigger-open');
                }
                return true;
            } else {

                var menuElm = $('.main-menu .has-children');

                if($('.sub-menu ul', menuElm).hasClass('left-menu')){
                    $('.sub-menu ul', menuElm).removeClass('left-menu');
                }
                return false;
            }
        }

        function moveNavigation(){
            var navigation = $('.menu--main'),
                desktop = checkWindowWidth();

            if ( desktop ) {
                $('#mobile-nav').children().remove();
                $('.menu-wrapper-left').html(menuLeft);
                $('.menu-wrapper-right').html(menuRight);
            } else {
                navigation.appendTo('#mobile-nav');
                $('.menu--main').not(':first').remove().children('li').appendTo('.menu--main:first');
            }
        }

        $(".menu-trigger").click(function() {
            $(this).toggleClass("menu-trigger-open");
        });
    });
}(jQuery));

If you want to see it in action, I've made a Codepen (try resize to see it working)

http://codepen.io/thehung1724/full/JYmzWr/

I hope I could explain well my problem. I've searched and didn't found a solution for this issue (or maybe I just didn't know really well what to look for). Although when you resize the screen, you can still click on the menu icon, but please notice it doesn't transform to 'X' letter and you can't click to show sub-menu

Works

enter image description here

Doesn't work

enter image description here

Any help would be appreciated!

The Hung
  • 309
  • 4
  • 18
  • The mobile menu button works as expected before, and also after a resize. Making the browser back to desktop size, and trying again still works, and resizing it once more to mobile sizing still works. I'm not sure what the issue is. – Pablo Canseco Nov 09 '15 at 14:00
  • @PabloCanseco It's work but notice you can't click to show sub menu and the menu trigger doesn't transform to 'X' letter. – The Hung Nov 09 '15 at 14:02
  • When you move or replace html you lose events that were bound to it. Use event delegation – charlietfl Nov 09 '15 at 14:21
  • @charlietfl Hi, could you please tell me how to use event delegation?. I'm newbie on jQuery. Thanks in advance. :) – The Hung Nov 09 '15 at 14:22
  • Not hard to research, try http://stackoverflow.com/questions/8110934/direct-vs-delegated-jquery-on – charlietfl Nov 09 '15 at 14:26
  • @charlietfl Thank you. I've tried `$('.header-bottom').on('click', '.menu-trigger', function(event)` but it doesn't work for me. – The Hung Nov 09 '15 at 14:30
  • Does `$('.header-bottom')` always exist and not get moved? Whatever you bind to must be a permanent asset. Can go all the way up to `document` if needed – charlietfl Nov 09 '15 at 14:32
  • @charlietfl Yes it always exist, I only move the `ul` child in `menu-wrapper` – The Hung Nov 09 '15 at 14:33

1 Answers1

3

An easy fix would be to replace

$('.has-children').on('click', function(event){

with

$(document).on('click', '.has-children', function (event) {

When you clone the nav from one place to another you are losing the pointer to the on click function for .has-children

With the $(document).on.... you're actually binding the function to the document which doesn't get disposed.


Update: Replace the following section of javascript

        $('.has-children').on('click', function (event) {
            var selected = $(this);
            if (selected.children('ul').hasClass('is-hidden')) {
                selected.children('ul').removeClass('is-hidden');
            }
        });
        $('.go-back').on('click', function (evt) {
            evt.stopPropagation();
            $(this).parent('ul').addClass('is-hidden').parent('.has-children').parent('ul');
        });

With

    $(document).on('click', '.has-children', function (event) {
        var selected = $(this);
        if (selected.children('ul').hasClass('is-hidden')) {
            selected.children('ul').removeClass('is-hidden');
        }
    });
    $(document).on('click', '.go-back', function (evt) {
        evt.stopPropagation();
        $(this).parent('ul').addClass('is-hidden').parent('.has-children').parent('ul');
    });

Further update: I've Forked your code in CodePen with the above changes: http://codepen.io/anon/pen/JYmVXm

Darren Gourley
  • 1,798
  • 11
  • 11
  • Thank you. I've tried this solution but it doesn't work for me. – The Hung Nov 09 '15 at 14:35
  • Interesting.. I've got it working on my machine with those changes. Let me modify my answer to show you exactly what to change.. – Darren Gourley Nov 09 '15 at 14:37
  • 1
    Here is a fork of your code with these changes in codepen: http://codepen.io/anon/pen/JYmVXm – Darren Gourley Nov 09 '15 at 14:46
  • Thank you so much @Darren. It's totally solved my issue. Could you explain about my issue? – The Hung Nov 09 '15 at 14:51
  • 2
    With your original method you are binding a click event to objects with class 'has-children'. When you resize the screen you are removing the elements and re-adding them, thus losing the binding (on removal). With the $(document).on... approach you are binding the click event to the document (the current page), so when you remove the .has-children object you're not removing the triggers as well. The new js listens for clicks on the 'document' and then specifically on any objects that match the selector in the second parameter, which in this case is '.has-children'. – Darren Gourley Nov 09 '15 at 14:56
  • Thank you so much. Very nice explanation. – The Hung Nov 09 '15 at 15:57
  • Thanks man this ($(document).on('click....) Function just solved each and every bug on my jquerys :D thanks mans – Rodrigo Zuluaga Jun 30 '18 at 19:18