Javascript-форум (https://javascript.ru/forum/)
-   jQuery (https://javascript.ru/forum/jquery/)
-   -   Как заставить сработать условие, после добавление класса (https://javascript.ru/forum/jquery/78832-kak-zastavit-srabotat-uslovie-posle-dobavlenie-klassa.html)

Luther 11.11.2019 01:14

Как заставить сработать условие, после добавление класса
 
Доброго времени суток. Проблема следующая: есть блок, изначально его позиция скрыта от визуального контакта right: -300, при клике, к блоку подключается class "mobile-menu--active", после чего его позиция должна равняться right: 0, но этого не происходит. Как исправить это, условие вроде написано правильно, но мне кажется оно срабатывает один раз и дальше не проверяется, как отследить добавление нового класса и проиграть условие повторно? Не судите строго, я только учусь:)
(function($) {
    
    $.fn.mobileMenu = function(options) {

        //Defaults to extend options
        var settings = {
            side: 'right',
            width: 300,
            animationSpeed: 600
        };

        //Extend those options
        var options = $.extend(settings, options);

        return this.each(function () {

            var mob = $(this),
                mobMenu = $('.mobile-menu');

            mob.on('click touchend', function () {

                if(mobMenu.is('.mobile-menu--active')) {
                    mobMenu.removeClass('mobile-menu--active');
                    $('.hamburger--active').removeClass('hamburger--active');
                } else {
                    mobMenu.addClass('mobile-menu--active');
                    mob.addClass('hamburger--active');
                }
                return false;
            });


/////// ANIMATION
            var rightSide = 'mobile-menu--right-side',
                leftSide = 'mobile-menu--left-side',
                topSide = 'mobile-menu--top-side';

            if(options.side == "right") {
                mobMenu
                    .addClass(rightSide)
                    .css({"right": - options.width, "z-index" : 3});
                if (mobMenu.is('mobile-menu--active')) {
                    mobMenu
                        .animate({"right": 0}, options.animationSpeed);
                }
            }
        }); //each call
    }; //mobileMenu plugin

})(jQuery);

рони 11.11.2019 03:33

Luther,
строки 29 - 30 перенести в начало строки 47, если не поможет, делайте макет.

[html run]
... минимальный код страницы с вашей проблемой
[/html]

О том, как вставить в сообщение исполняемый javascript и html-код, а также о дополнительных возможностях форматирования - читайте http://javascript.ru/formatting.

Luther 11.11.2019 13:04

Перенес как вы сказали, не помогло, прикрепляю код, надеюсь все правильно сделал.

<!DOCTYPE html>
<html lang="ru">
<html>
    <head>
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
.hamburger {width: 20px;height: 20px;background-color: red}
.hamburger--active {background-color: green}

.mobile-menu {position: fixed;width: 100%;max-width: 300px;height: 100%; top: 0}
</style>
</head>
<body>
<div class="hamburger">
</div>
<div class="mobile-menu">
<h1>Заголовок блока</h1>
<p>Далеко-далеко за словесными горами в стране, гласных и согласных живут рыбные тексты. Проектах знаках переулка курсивных снова журчит жизни все приставка безопасную!</p>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
(function($) {
    
    $.fn.mobileMenu = function(options) {

        //Defaults to extend options
        var settings = {
            side: 'right',
            width: 300,
            animationSpeed: 600
        };

        //Extend those options
        var options = $.extend(settings, options);

        return this.each(function () {

            var mob = $(this),
                mobMenu = $('.mobile-menu');

            mob.on('click touchend', function () {

                if(mobMenu.is('.mobile-menu--active')) {
                    mobMenu.removeClass('mobile-menu--active');
                    $('.hamburger--active').removeClass('hamburger--active');
                } else {
                    mobMenu.addClass('mobile-menu--active');
                    mob.addClass('hamburger--active');
                }
                return false;
            });


/////// ANIMATION
            var rightSide = 'mobile-menu--right-side',
                leftSide = 'mobile-menu--left-side',
                topSide = 'mobile-menu--top-side';

            if(options.side == "right") {
                mobMenu
                    .addClass(rightSide)
                    .css({"right": - options.width, "z-index" : 3});
                if (mobMenu.is('mobile-menu--active')) {
                    mobMenu
                        .animate({"right": 0}, options.animationSpeed);
                }
            }
        }); //each call
    }; //mobileMenu plugin

})(jQuery);

$('.hamburger').mobileMenu({
    });
</script>
</body>
</html>

laimas 11.11.2019 14:24

Luther, у вас же не css анимация, а изменение класса, это асинхронное событие. Тогда сразу по смене класса и выполняйте анимацию:

(function($) {
    
    $.fn.mobileMenu = function(options) {

        var settings = {
            side: 'right',
            width: 300,
            animationSpeed: 600
        };

        //Extend those options
        var options = $.extend(settings, options);

        return this.each(function () {

            var mob = $(this),
                mobMenu = $('.mobile-menu');

            mob.on('click touchend', function () {

                mobMenu.toggleClass('mobile-menu--active');
                mob.toggleClass('hamburger--active');
                mobMenu.animate({right: -options.width + mob.is('.hamburger--active') * options.width}, options.animationSpeed);
                
            });
        });
    };
})(jQuery);

$('.hamburger').mobileMenu();

Luther 11.11.2019 15:27

Цитата:

Сообщение от laimas (Сообщение 515358)
Luther, у вас же не css анимация, а изменение класса, это асинхронное событие. Тогда сразу по смене класса и выполняйте анимацию:

(function($) {
    
    $.fn.mobileMenu = function(options) {

        var settings = {
            side: 'right',
            width: 300,
            animationSpeed: 600
        };

        //Extend those options
        var options = $.extend(settings, options);

        return this.each(function () {

            var mob = $(this),
                mobMenu = $('.mobile-menu');

            mob.on('click touchend', function () {

                mobMenu.toggleClass('mobile-menu--active');
                mob.toggleClass('hamburger--active');
                mobMenu.animate({right: -options.width + mob.is('.hamburger--active') * options.width}, options.animationSpeed);
                
            });
        });
    };
})(jQuery);

$('.hamburger').mobileMenu();


Оказывается это было проще, чем то что я варганил, спасибо большое за помощь, все работает

laimas 11.11.2019 15:46

Цитата:

Сообщение от Luther
все работает

Странно как-то у вас это работает (определяется). В CSS задается макс ширина, и в опциях можно передать ширину и надо полагать позицию (side: 'right'). Если так, то ведь придется менять и CSS. Если параметры меню можно определять опциями, значит в CSS нужно задать только основные, то есть до инициализации, все остальное в плагине. К тому же странно плагин определять не основному, то есть меню, а кнопке. По идее блоку нужно определять, а кнопку можно переопределять также через опции, при этом блок скрывается изначально, чтобы "не моргал" при загрузке страницы.

<!DOCTYPE html>
<html lang="ru">
<html>
    <head>
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
.hamburger-button {width: 20px;height: 20px;background-color: red}
.hamburger--active {background-color: green}

.hamburger {
    position: fixed;
    top: 20px;
    height: 100%;
    display: none; 
}
</style>
</head>
<body>
<div class="hamburger-button">
</div>
<div class="hamburger">
<h1>Заголовок блока</h1>
<p>Далеко-далеко за словесными горами в стране, гласных и согласных живут рыбные тексты. Проектах знаках переулка курсивных снова журчит жизни все приставка безопасную!</p>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
(function($) {
    
    $.fn.mobileMenu = function(options) {

        var settings = {
            side: 'right',
            width: 300,
            animationSpeed: 600,
            button: $('div.hamburger-button') 
        };

        var options = $.extend(settings, options), mobMenu = $(this);
        
        mobMenu.css({maxWidth: options.width, [options.side]: -options.width}).show(); //так определить в литеральном объекте свойство через переменную можно в ES6
        
        return this.each(function () {

            options.button.on('click touchend', function () {
                options.button.toggleClass('hamburger--active');
                 //и только так определить в литеральном объекте свойство через переменную можно для старичков
                var opt = {};
                opt[options.side] = -options.width + options.button.is('.hamburger--active') * options.width; 
                mobMenu.animate(opt, options.animationSpeed);
                
            });
        });

    };
})(jQuery);

$('div.hamburger').mobileMenu({side: 'left', width: 600});
</script>
</body>
</html>

рони 11.11.2019 15:57

:) потом будет вопрос как закрыть по клику вне меню.

laimas 11.11.2019 15:58

рони, :D

Luther 12.11.2019 00:51

Цитата:

Сообщение от laimas (Сообщение 515371)
Странно как-то у вас это работает (определяется). В CSS задается макс ширина, и в опциях можно передать ширину и надо полагать позицию (side: 'right'). Если так, то ведь придется менять и CSS. Если параметры меню можно определять опциями, значит в CSS нужно задать только основные, то есть до инициализации, все остальное в плагине. К тому же странно плагин определять не основному, то есть меню, а кнопке. По идее блоку нужно определять, а кнопку можно переопределять также через опции, при этом блок скрывается изначально, чтобы "не моргал" при загрузке страницы.

<!DOCTYPE html>
<html lang="ru">
<html>
    <head>
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
.hamburger-button {width: 20px;height: 20px;background-color: red}
.hamburger--active {background-color: green}

.hamburger {
    position: fixed;
    top: 20px;
    height: 100%;
    display: none; 
}
</style>
</head>
<body>
<div class="hamburger-button">
</div>
<div class="hamburger">
<h1>Заголовок блока</h1>
<p>Далеко-далеко за словесными горами в стране, гласных и согласных живут рыбные тексты. Проектах знаках переулка курсивных снова журчит жизни все приставка безопасную!</p>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
(function($) {
    
    $.fn.mobileMenu = function(options) {

        var settings = {
            side: 'right',
            width: 300,
            animationSpeed: 600,
            button: $('div.hamburger-button') 
        };

        var options = $.extend(settings, options), mobMenu = $(this);
        
        mobMenu.css({maxWidth: options.width, [options.side]: -options.width}).show(); //так определить в литеральном объекте свойство через переменную можно в ES6
        
        return this.each(function () {

            options.button.on('click touchend', function () {
                options.button.toggleClass('hamburger--active');
                 //и только так определить в литеральном объекте свойство через переменную можно для старичков
                var opt = {};
                opt[options.side] = -options.width + options.button.is('.hamburger--active') * options.width; 
                mobMenu.animate(opt, options.animationSpeed);
                
            });
        });

    };
})(jQuery);

$('div.hamburger').mobileMenu({side: 'left', width: 600});
</script>
</body>
</html>


Только щас увидел и чет не очень понятно, но я сделал следующем образом, если интересно:


<!DOCTYPE html>
<html lang="ru">
<html>
    <head>
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
.hamburger {width: 20px;height: 20px;background-color: red}
.hamburger--active {background-color: green}
.mobile-menu {position: fixed;height: 100%;top: 0;background-color: yellow}
.overlay {
    left: 0;
    width: 100%;
    background-color: rgba(0,0,0,.4);
    position: fixed;
    top: 0;
    height: 100%;
}

</style>
</head>
<body>
<div class="hamburger">
</div>
<div class="mobile-menu">
   <h1>Заголовок</h1>
   <p>  Далеко-далеко за словесными горами в стране, гласных и согласных живут рыбные тексты. Запятой lorem текста, первую ему ее, над родного. Если, алфавит ручеек коварный там города образ великий маленькая текстов вопроса он.</p>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
(function($) {
    
    $.fn.mobileMenu = function(options) {

        //Defaults to extend options
        var settings = {
            side: 'left',
            butClass: 'hamburger',
            butClassActive: 'hamburger--active',
            width: 300, // if puted (side: 'top'), there must be 100
            zIndex: 99,
            exit: true,
            overlay: true,
            animationSpeed: 400,
            // afterChange: function(){}
        };

        //Extend those options
        var options = $.extend(settings, options);

        return this.each(function () {
            var mob = $(this),
                btn = $('.' + options.butClass),
                mobMenu = $('.mobile-menu'),
                mobMenuActive = 'mobile-menu--active';

            var rightSide = 'mobile-menu--right-side',
                leftSide = 'mobile-menu--left-side',
                topSide = 'mobile-menu--top-side';


            if (options.side == 'right') {
                mobMenu.addClass(rightSide).css({right: -options.width, width: options.width, 'z-index': options.zIndex});
            }
            if (options.side == 'left') {
                mobMenu.addClass(leftSide).css({left: -options.width, width: options.width, 'z-index': options.zIndex});
            }
            if (options.side == 'top') {
                mobMenu.addClass(topSide).css({top: -options.width+'%', width: options.width+'%', 'z-index': options.zIndex});
            }

            function moveMenu() {
                if (options.side == 'right') {
                    $('.' + rightSide).animate({right: -options.width + mob.hasClass(options.butClassActive) * options.width}, options.animationSpeed);
                }
                if (options.side == 'left') {
                    $('.' + leftSide).animate({left: -options.width + mob.hasClass(options.butClassActive) * options.width}, options.animationSpeed);
                }
                if (options.side == 'top') {
                    $('.' + topSide).animate({top: -options.width + mob.hasClass(options.butClassActive) * options.width+'%'}, options.animationSpeed);
                }
            }

            if (options.overlay) {
                if (options.overlay == "false") { return false; }
                var directionalOverlayHTML = '<div class="overlay"></div>';
                mobMenu.before(directionalOverlayHTML);

                var bgOverlay = $('div.overlay');
                bgOverlay.hide().css({'z-index': options.zIndex - 1});
            }


            mob.on('click touchend', function () {
                mobMenu.stop().toggleClass(mobMenuActive);
                btn.stop().toggleClass(options.butClassActive);
                if (options.overlay) {
                    if(options.overlay == "false") { return false; }

                    if (mobMenu.hasClass(mobMenuActive)) {
                        if (btn.hasClass(options.butClassActive)) {
                            bgOverlay.fadeIn(options.animationSpeed);
                            bgOverlay.on('click touchend', function () {
                                mobMenu.stop().removeClass(mobMenuActive);
                                btn.stop().removeClass(options.butClassActive);
                                bgOverlay.fadeOut(options.animationSpeed);
                                moveMenu();
                            });
                        }
                    }
                }
                moveMenu();
            });

            if (options.exit) {

                if(options.exit == "false") { return false; }

                var directionalExitHTML = '<div class="mobile-menu-inner"><span class="mobile-menu__exit">exit</span></div>';

                mobMenu.append(directionalExitHTML);

                var exitBtn = mobMenu.children('div.mobile-menu-inner').children('span.mobile-menu__exit');

                exitBtn.on('click touchend', function () {
                    mobMenu.stop().removeClass(mobMenuActive);
                    btn.stop().removeClass(options.butClassActive);
                    moveMenu();
                    if (options.overlay) {
                        if(options.overlay == "false") { return false; }
                        bgOverlay.fadeOut(options.animationSpeed);
                    }
                });
            }

            $(document).keyup(function(esc) {
                if (esc.keyCode === 27) {
                    if (mobMenu.hasClass(mobMenuActive)) {
                        if (btn.hasClass(options.butClassActive)) {
                            mobMenu.stop().removeClass(mobMenuActive);
                            btn.stop().removeClass(options.butClassActive);
                            moveMenu();
                            if (options.overlay) {
                                if(options.overlay == "false") { return false; }
                                bgOverlay.fadeOut(options.animationSpeed);
                            }
                        }
                    }
                }
            });
        }); //each call
    }; //mobileMenu plugin

})(jQuery);

$('.hamburger').mobileMenu();
</script>
</body>
</html>
[/quote]

Да, я думаю код в некоторых местах некорректный, ну как получается, сейчас пытаюсь настроить анимацию так чтоб она не прыгала, при многократном нажатии, добавил .stop(), но работает не очень привлекательно, как можно сделать, чтоб анимация проигрывалась до конца, а не постоянно обрабатывалась? + еще надо , чтобы на .hamburger вешался класс .hamburger--active только после того как анимация проигралась, ну т.е. Зеленой, кнопка должна стать, только когда меню окончательно выдвинулось

laimas 12.11.2019 01:10

Зачем 'z-index': options.zIndex все время определять, а не единожды? Выполните для позиции лево/верх, и сразу будет виден баг. И почему вы упорно определяете кнопку управления как целевой объект для плагина вместо самого меню?

И лишнего много.

рони 12.11.2019 01:19

Luther,
на всякий случай
закрытие блока по клику вне его или крестику

laimas 12.11.2019 01:56

Luther, надо определиться с кнопкой, а сколько классов и зачем добавлять меню, это уже вторично. Меню можно и снизу/вверх, сверху/вниз, слева/направо, справа/налево сдвигать по одному и тому же принципу, его начальная позиция и будет определять все параметры, как то классы меню которые вы описываете, и положение кнопки, и для этого описаний условий if(...) ... не потребуется, проще взять определения из объекта. Положение кнопки для того, чтобы к примеру она перекрывала меню, и ее положение зависело от исходного положения меню как тут:

<!DOCTYPE html>
<html lang="ru">
<html>
    <head>
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
html,body {
    height: 100%;
    margin: 0;
}

.hamburger-button {
    display: none;
    position: fixed;
    top: 20px;
    width: 30px; 
    height: 30px;
    cursor: pointer;
    z-index: 1;
}

.button-bar1, .button-bar2, .button-bar3 {
    width: 30px;
    height: 5px;
    margin: 0 0 5px;
    background-color: #888;
}

.hamburger--active .button-bar1 {
    background-color: #fff;
    transform: rotate(-45deg) translate(-8px, 7px);
}

.hamburger--active .button-bar2 {
    opacity: 0;
}

.hamburger--active .button-bar3 {
    background-color: #fff;
    transform: rotate(45deg) translate(-7px, -7px);
}


.hamburger {
    position: fixed;
    height: 100%;
    top: 0;
    display: none;
    background-color: #999;
    padding: 20px;
    box-sizing: border-box 
}
</style>
</head>
<body>
<div class="hamburger-button">
  <div class="button-bar1"></div>
  <div class="button-bar2"></div>
  <div class="button-bar3"></div>
</div>
<div class="hamburger">
<h1>Заголовок блока</h1>
<p>Далеко-далеко за словесными горами в стране, гласных и согласных живут рыбные тексты. Проектах знаках переулка курсивных снова журчит жизни все приставка безопасную!</p>
</div>
<script>

</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
(function($) {
$.fn.mobileMenu = function(options) {

    var settings = {
        side: 'right',
        width: 300,
        animationSpeed: 600,
        zIndex: 100,
        button: '.hamburger-button',
        classActive: 'hamburger--active' 
    }, options = $.extend(settings, options), mobMenu = $(this), bt = $(options.button);
    
    mobMenu.css({maxWidth: options.width, [options.side]: -options.width, zIndex: options.zIndex}).show();
    bt.css({top: mobMenu.css('padding-top'), [options.side]: mobMenu.css('padding-top'), zIndex: options.zIndex+1}).show().children().css({transition: options.animationSpeed/1000 + 's'});
        
    $(document).on('click', function(e) {
        e = $(e.target);
        if(!parseInt(mobMenu[0].style[options.side]) && !(e.closest(mobMenu).length || e.hasClass(options.classActive))) bt.trigger('click', [1]);
    });
        
    return this.each(function () {

        bt.on('click touchend', function (e, c) {
            
            e.stopPropagation();
            bt.toggleClass(options.classActive);
            mobMenu.animate({[options.side]: -options.width + (c ? 0 : bt.hasClass(options.classActive)) * options.width}, options.animationSpeed);
                
        })
    
    });
        
};
})(jQuery);

$('div.hamburger').mobileMenu({side: 'left', width: 400});
</script>
</body>
</html>


Но если направление вверх/вниз, то вряд ли разумно options.width+'%', скорее всего высота меню должна при инициализации получать высоту клиентской области, ее же и при изменении размеров окна. Изменять класс кнопки после окончания анимации меню, это еще вопрос нужно ли, то есть определитесь окончательно со всем, тогда и остальное будет видно.

Luther 12.11.2019 03:12

Цитата:

Сообщение от laimas (Сообщение 515387)
Luther, надо определиться с кнопкой, а сколько классов и зачем добавлять меню, это уже вторично. Меню можно и снизу/вверх, сверху/вниз, слева/направо, справа/налево сдвигать по одному и тому же принципу, его начальная позиция и будет определять все параметры, как то классы меню которые вы описываете, и положение кнопки, и для этого описаний условий if(...) ... не потребуется, проще взять определения из объекта. Положение кнопки для того, чтобы к примеру она перекрывала меню, и ее положение зависело от исходного положения меню как тут:

<!DOCTYPE html>
<html lang="ru">
<html>
    <head>
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
html,body {
    height: 100%;
    margin: 0;
}

.hamburger-button {
    display: none;
    position: fixed;
    top: 20px;
    width: 30px; 
    height: 30px;
    cursor: pointer;
    z-index: 1;
}

.button-bar1, .button-bar2, .button-bar3 {
    width: 30px;
    height: 5px;
    margin: 0 0 5px;
    background-color: #888;
}

.hamburger--active .button-bar1 {
    background-color: #fff;
    transform: rotate(-45deg) translate(-8px, 7px);
}

.hamburger--active .button-bar2 {
    opacity: 0;
}

.hamburger--active .button-bar3 {
    background-color: #fff;
    transform: rotate(45deg) translate(-7px, -7px);
}


.hamburger {
    position: fixed;
    height: 100%;
    top: 0;
    display: none;
    background-color: #999;
    padding: 20px;
    box-sizing: border-box 
}
</style>
</head>
<body>
<div class="hamburger-button">
  <div class="button-bar1"></div>
  <div class="button-bar2"></div>
  <div class="button-bar3"></div>
</div>
<div class="hamburger">
<h1>Заголовок блока</h1>
<p>Далеко-далеко за словесными горами в стране, гласных и согласных живут рыбные тексты. Проектах знаках переулка курсивных снова журчит жизни все приставка безопасную!</p>
</div>
<script>

</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
(function($) {
$.fn.mobileMenu = function(options) {

    var settings = {
        side: 'right',
        width: 300,
        animationSpeed: 600,
        zIndex: 100,
        button: '.hamburger-button',
        classActive: 'hamburger--active' 
    }, options = $.extend(settings, options), mobMenu = $(this), bt = $(options.button);
    
    mobMenu.css({maxWidth: options.width, [options.side]: -options.width, zIndex: options.zIndex}).show();
    bt.css({top: mobMenu.css('padding-top'), [options.side]: mobMenu.css('padding-top'), zIndex: options.zIndex+1}).show().children().css({transition: options.animationSpeed/1000 + 's'});
        
    $(document).on('click', function(e) {
        e = $(e.target);
        if(!parseInt(mobMenu[0].style[options.side]) && !(e.closest(mobMenu).length || e.hasClass(options.classActive))) bt.trigger('click', [1]);
    });
        
    return this.each(function () {

        bt.on('click touchend', function (e, c) {
            
            e.stopPropagation();
            bt.toggleClass(options.classActive);
            mobMenu.animate({[options.side]: -options.width + (c ? 0 : bt.hasClass(options.classActive)) * options.width}, options.animationSpeed);
                
        })
    
    });
        
};
})(jQuery);

$('div.hamburger').mobileMenu({side: 'left', width: 400});
</script>
</body>
</html>


Но если направление вверх/вниз, то вряд ли разумно options.width+'%', скорее всего высота меню должна при инициализации получать высоту клиентской области, ее же и при изменении размеров окна. Изменять класс кнопки после окончания анимации меню, это еще вопрос нужно ли, то есть определитесь окончательно со всем, тогда и остальное будет видно.


Ваш код отлично выглядит, работает тоже прекрасно, завтра буду разбираться, большое спасибо что помогаете

laimas 12.11.2019 03:48

Ну в таком случае, кнопку меню можно также сдвигать к краю, влево/вправо в зависимости от положения меню. При этом ее анимацию (open/close) и анимацию движения можно задерживать, а к движению применять различные временные характеристики анимации.

В коде в литеральном объекте свойство определяется как в E6, о чем говорилось выше. Но, к примеру, если сжать код, например этим, то в итоге все будет отвечать и старичкам, тут как раз итог сжатия. А в коде приведенном ранее добавление и изменение после bt = $(options.button) добавлено объявление p = mobMenu.css('padding-top'), изменено

bt.css({top: p, [options.side]: p, zIndex: options.zIndex+1}).show().children().css({transition: options.animationSpeed/1000 + 's'});


В обработчик bt.on('click touchend' ... добавлено:

var r = parseInt(p);
            bt.toggleClass(options.classActive).delay(50).animate({[options.side]: r + (options.width - (r*3.5)) * (c ? 0 : bt.hasClass(options.classActive))}, options.animationSpeed);


<!DOCTYPE html>
<html lang="ru">
<html>
    <head>
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
html,body {
    height: 100%;
    margin: 0;
}

.hamburger-button {
    display: none;
    position: fixed;
    top: 20px;
    width: 30px; 
    height: 30px;
    cursor: pointer;
    z-index: 1;
}

.button-bar1, .button-bar2, .button-bar3 {
    width: 30px;
    height: 5px;
    margin: 0 0 5px;
    background-color: #888;
    transition-delay: .3s
}

.hamburger--active .button-bar1 {
    background-color: #fff;
    transform: rotate(-45deg) translate(-8px, 7px);
}

.hamburger--active .button-bar2 {
    opacity: 0;
}

.hamburger--active .button-bar3 {
    background-color: #fff;
    transform: rotate(45deg) translate(-7px, -7px);
}


.hamburger {
    position: fixed;
    height: 100%;
    top: 0;
    display: none;
    background-color: #999;
    padding: 20px;
    box-sizing: border-box 
}
</style>
</head>
<body>
<div class="hamburger-button">
  <div class="button-bar1"></div>
  <div class="button-bar2"></div>
  <div class="button-bar3"></div>
</div>
<div class="hamburger">
<h1>Заголовок блока</h1>
<p>Далеко-далеко за словесными горами в стране, гласных и согласных живут рыбные тексты. Проектах знаках переулка курсивных снова журчит жизни все приставка безопасную!</p>
</div>
<script>

</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
(function(c){c.fn.mobileMenu=function(a){a=c.extend({side:"right",width:300,animationSpeed:600,zIndex:100,button:".hamburger-button",classActive:"hamburger--active"},a);var e=c(this),d=c(a.button),g=e.css("padding-top"),b={};e.css((b.maxWidth=a.width,b[a.side]=-a.width,b.zIndex=a.zIndex,b)).show();b={};d.css((b.top=g,b[a.side]=g,b.zIndex=a.zIndex+1,b)).show().children().css({transition:a.animationSpeed/1E3+"s"});c(document).on("click",function(b){b=c(b.target);parseInt(e[0].style[a.side])||b.closest(e).length||b.hasClass(a.classActive)||d.trigger("click",[1])});return this.each(function(){d.on("click touchend",function(b,c){b.stopPropagation();var f=parseInt(g),h={};d.toggleClass(a.classActive).delay(50).animate((h[a.side]=f+(a.width-3.5*f)*(c?0:d.hasClass(a.classActive)),h),a.animationSpeed);f={};e.animate((f[a.side]=-a.width+(c?0:d.hasClass(a.classActive))*a.width,f),a.animationSpeed)})})}})(jQuery);

$('div.hamburger').mobileMenu({width: 400});
</script>
</body>
</html>

рони 12.11.2019 07:57

Luther,
не копируйте сообщение целиком без необходимости, есть цитирование выделенного фрагмента.

Luther 12.11.2019 14:38

Цитата:

Сообщение от laimas (Сообщение 515387)

$(document).on('click', function(e) {
        e = $(e.target);
        if(!parseInt(mobMenu[0].style[options.side]) && !(e.closest(mobMenu).length || e.hasClass(options.classActive))) bt.trigger('click', [1]);
    });

Здравствуйте, да код отличный, пытаюсь разобраться, вопрос вызывает эта функция, а именно строчка:
if(!parseInt(mobMenu[0].style[options.side]) && !(e.closest(mobMenu).length || e.hasClass(options.classActive))) bt.trigger('click', [1]);
Не могли бы вы объяснить ее? И я буду немного дорабаывать/переделывать, кнопка будет оставаться на своем месте и не налазить на меню, но ее анимация должна проигрываться, но только после открытия меню или закрытия, а в самом меню сделать дополнительную кнопку закрытия. Спасибо что помогаете

рони 12.11.2019 14:54

Цитата:

Сообщение от Luther
но ее анимация должна проигрываться, но только после открытия меню или закрытия,

.animate( properties [, duration ] [, easing ] [, complete ] )
Цитата:

функция, которая будет вызвана после завершения анимации.

laimas 12.11.2019 15:21

Это закрытие меню при клике вне его. Здесь запускается событие click кнопки, в обработчик которого передается параметр 1. Это произойдет если меню раскрыто (!parseInt(mobMenu[0].style[options.side])) И источник не принадлежит меню (e.closest(mobMenu).length) ИЛИ это не кнопка меню (e.hasClass(options.classActive)).

В обработчике меню сдвигается от исходного положения (side) в направлении рассчитанным:

для кнопки: отступ меню + (ширина меню - отступ меню*3.5) * направление определяемое условием щелчка вне меню либо состоянием кнопки меню

для меню: отрицательная ширина меню + направление определяемое условием щелчка вне меню либо состоянием кнопки меню * ширина меню

Направление определяемое условием щелчка вне меню либо состоянием кнопки меню * ширина меню = c ? 0 : bt.hasClass(options.classActive). Можно в триггере в обработчик передавать значение -1, тогда это условие можно записать так:

c || bt.hasClass(options.classActive)

но в этом случае будет погрешность в 1px при сдвиге меню (можете вывести в консоль, поймете).

Если результат выводить в консоль, то станет понятна вся эта арифметика, получение -NNN или 0 для меню и NNN или 20 для кнопки.

Для кнопки и меню одно и тоже условие, значит его можно определить сразу. Но определение будет до изменения состояния кнопки меню, следовательно в этом случае нужно брать отрицание состояния кнопки.

return this.each(function () {

        bt.on('click touchend', function (e, c) {
            
            e.stopPropagation(); 
            var r = parseInt(p), 
            dir = c ? 0 : !bt.hasClass(options.classActive); //направление в анимации
            bt.toggleClass(options.classActive).delay(50).animate({[options.side]: r + (options.width - r*3.5) * dir}, options.animationSpeed);
            mobMenu.animate({[options.side]: -options.width + dir * options.width}, options.animationSpeed);
                
        })
    
    });


Это пример, и в примере время анимации состояния кнопки определяется временем анимации меню. Но на мой взгляд, если принять такое поведение меню, то за время анимации меню кнопка должна только сдвигаться к краю и изменять цвет, а после этого уже анимация состояния кнопки.

Это пример и в нем все упрощено, у вас же все написано так, что ... ) На Хабре, да и не только на нем, можно найти статьи о создании плагинов в jQ, там описано как это грамотно сделать - параметры, инициализация, методы.

Luther 12.11.2019 16:19

Цитата:

Сообщение от laimas (Сообщение 515412)
Это закрытие меню при клике вне его. Здесь запускается событие click кнопки, в обработчик которого передается параметр 1. Это произойдет если меню раскрыто (!parseInt(mobMenu[0].style[options.side])) И источник не принадлежит меню (e.closest(mobMenu).length) ИЛИ это не кнопка меню (e.hasClass(options.classActive)).
.

Спасибо за объяснение, добавил по аналогу еще 2 функции
var exit = $(options.exit);

$(document).keyup(function (e) {
            if(e.keyCode === 27 && !parseInt(mobMenu[0].style[options.side])) bt.trigger('click', [1]);
        });

        exit.on('click', function (e) {
           e = $(e.target);
           if(!parseInt(mobMenu[0].style[options.side])) bt.trigger('click', [1]);
        });


Работает, только приходиться дублировать, есть ли возможность объединения?

Luther 12.11.2019 16:21

Цитата:

Сообщение от рони (Сообщение 515411)
.animate( properties [, duration ] [, easing ] [, complete ] )

Спасибо, попробую этот способ

laimas 12.11.2019 18:29

Цитата:

Сообщение от Luther
приходиться дублировать, есть ли возможность объединения?

Можно и одним обработчиком, в нем ведь будет известен и тип события, и его источник. В какофонии сообщений я не заметил этого "кнопка будет оставаться на своем месте и не налазить на меню, но ее анимация должна проигрываться, но только после открытия меню или закрытия". Не проблема, но наверное ее лучше запускать с определенного шага анимации (step), а не в конце ее. Например, пусть запуск анимации, это наличие класса 'hamburger--animate' у дочерних элементов кнопки, то есть:

.hamburger--animate .button-bar1 {
    background-color: #fff;
    transform: rotate(-45deg) translate(-8px, 7px);
}

.hamburger--animate .button-bar2 {
    opacity: 0;
}

.hamburger--animate .button-bar3 {
    background-color: #fff;
    transform: rotate(45deg) translate(-7px, -7px);
}


которые добавляются им на определенном шаге анимации меню. При определении объектов определяем для меню данные, ну и запуск в шаге:

mobMenu = $(this).data({end: 1})

        bt.on('click touchend', function (e, c) {
            
            e.stopPropagation(); 
            var dir = c ? 0 : !bt.hasClass(options.classActive); //направление в анимации
            bt.toggleClass(options.classActive)
            mobMenu.animate({[options.side]: -options.width + options.width * dir}, {
                duration: options.animationSpeed, 
                step: function(pos, f) {
                    var v = f.start + f.end, 
                        d = dir ? v - v * .3 //старт анимации кнопки при открытии 
                                : v * .5; //старт анимации кнопки при закрытии
                    if($(this).data('end') && pos > d) {
                        bt.toggleClass('hamburger--animate'); //включаем анимацию/возвращаем в начало
                        $(this).data({end: 0}) //более не реагируем на изменения
                    }
                },
                complete: function(f) {
                    $(this).data({end: 1}) //разрешаем запуск анимации кнопки
                }
            });
                
        })


Плюс различные временные характеристики анимации доступны, и тогда все может получится, по крайней мере лучше, чем по окончании анимации меню, на мой взгляд.

Luther 12.11.2019 21:48

Цитата:

Сообщение от laimas (Сообщение 515425)
Можно и одним обработчиком, в нем ведь будет известен и тип события, и его источник. В какофонии сообщений я не заметил этого "кнопка будет оставаться на своем месте и не налазить на меню, но ее анимация должна проигрываться, но только после открытия меню или закрытия". Не проблема, но наверное ее лучше запускать с определенного шага анимации (step), а не в конце ее. Например, пусть запуск анимации, это наличие класса 'hamburger--animate' у дочерних элементов кнопки, то есть:

Этот способ почему то не сработал, сделал пока что так
mobMenu.toggleClass(mobActive).stop().animate({[options.side]: -options.width + (c ? 0 : mobMenu.hasClass(mobActive)) * options.width}, {complete: function() {bt.toggleClass(btActive)}}, options.animationSpeed);


Но, все равно, это не так как нужно, потому что при многократном клике, кнопка будет меняться, не смотря на то что меню еще не сработало. В общем, вот как сейчас все выглядит, нужно только с кнопкой разобраться, а так все в порядке думаю

<!DOCTYPE html>
<html lang="ru">
<html>
    <head>
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
html,body {
    height: 100%;
    margin: 0;
}

.hamburger{
    position: fixed;
    top: 20px;
    width: 30px; 
    height: 30px;
    cursor: pointer;
    z-index: 1;
    transition: all .25s ease;
}

.button-bar1, .button-bar2, .button-bar3 {
    width: 30px;
    height: 5px;
    margin: 0 0 5px;
    background-color: #888;
    transition: all .25s ease;
}

.hamburger--active .button-bar1 {
    transform: rotate(-45deg) translate(-8px, 7px);
}

.hamburger--active .button-bar2 {
    opacity: 0;
}

.hamburger--active .button-bar3 {
    transform: rotate(45deg) translate(-7px, -7px);
}


.mobile-menu {
    position: fixed;
    height: 100%;
    top: 0;
    display: none;
    background-color: #999;
    padding: 20px;
    box-sizing: border-box 
}
</style>
</head>
<body>
<div class="hamburger">
  <div class="button-bar1"></div>
  <div class="button-bar2"></div>
  <div class="button-bar3"></div>
</div>
<div class="mobile-menu">
   <h1>Заголовок блока</h1>
   <p>Далеко-далеко за словесными горами в стране, гласных и согласных живут рыбные тексты. Проектах знаках переулка курсивных снова журчит жизни все приставка безопасную!</p>
   <div class="exit">Выйти</div>
</div>
<script>

</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
(function($) {
    $.fn.mobileMenu = function(options) {

        var settings = {
            side: 'right',
            width: 300,
            animationSpeed: 600,
            zIndex: 100,
            exit: '.exit',
            button: 'hamburger',
            classActive: '--active'
        }, options = $.extend(settings, options), mobMenu = $(this).data({end: 1}), bt = $('.' + options.button), mobActive = mobMenu.attr('class').split(' ')[0] + options.classActive, btActive = bt.attr('class').split(' ')[0] + options.classActive, exit = $(options.exit);


        mobMenu.css({maxWidth: options.width, [options.side]: -options.width, zIndex: options.zIndex}).show();

        $(document).on('click', function(e) {
            e = $(e.target);
            if(!parseInt(mobMenu[0].style[options.side]) && !(e.closest(mobMenu).length || e.hasClass(mobActive))) bt.trigger('click', [1]);
        });

        $(document).keyup(function (e) {
            if(e.keyCode === 27 && !parseInt(mobMenu[0].style[options.side])) bt.trigger('click', [1]);
        });

        exit.on('click', function (e) {
           e = $(e.target);
           if(!parseInt(mobMenu[0].style[options.side])) bt.trigger('click', [1]);
        });

        return this.each(function () {

            bt.on('click touchend', function (e, c) {
                e.stopPropagation();
                mobMenu.toggleClass(mobActive).stop().animate({[options.side]: -options.width + (c ? 0 : mobMenu.hasClass(mobActive)) * options.width}, {complete: function() {bt.toggleClass(btActive)}}, options.animationSpeed);
            })
        });
    };
})(jQuery);

$('div.mobile-menu').mobileMenu({side: 'right', width: 400});
</script>
</body>
</html>

laimas 12.11.2019 22:27

Цитата:

Сообщение от Luther
Но, все равно, это не так как нужно, потому что при многократном клике ...

<!DOCTYPE html>
<html lang="ru">
<html>
    <head>
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
html,body {
    height: 100%;
    margin: 0;
}

.hamburger-button {
    display: none;
    position: fixed;
    top: 20px;
    width: 30px; 
    height: 30px;
    cursor: pointer;
    z-index: 1;
}

.button-bar1, .button-bar2, .button-bar3 {
    width: 30px;
    height: 5px;
    margin: 0 0 5px;
    background-color: #777;
    transition: .6s
}

.hamburger--animate .button-bar1 {
    background-color: #fff;
    transform: rotate(-45deg) translate(-8px, 7px);
}

.hamburger--animate .button-bar2 {
    opacity: 0;
}

.hamburger--animate .button-bar3 {
    background-color: #fff;
    transform: rotate(45deg) translate(-7px, -7px);
}


.hamburger {
    position: fixed;
    height: 100%;
    top: 0;
    display: none;
    background-color: #999;
    padding: 20px;
    box-sizing: border-box 
}
</style>
</head>
<body>
<div class="hamburger-button">
  <div class="button-bar1"></div>
  <div class="button-bar2"></div>
  <div class="button-bar3"></div>
</div>
<div class="hamburger">
<h1>Заголовок блока</h1>
<p>Далеко-далеко за словесными горами в стране, гласных и согласных живут рыбные тексты. Проектах знаках переулка курсивных снова журчит жизни все приставка безопасную!</p>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>

(function($) {
$.fn.mobileMenu = function(options) {

    var settings = {
        side: 'right',
        width: 300,
        animationSpeed: 600,
        zIndex: 100,
        esc: true,
        button: '.hamburger-button',
        classActive: 'hamburger--active',
        classAnimateButton: 'hamburger--animate' 
    }, options = $.extend(settings, options), mobMenu = $(this).data({end: 1}), bt = $(options.button), p = mobMenu.css('padding-top');
    
    mobMenu.css({maxWidth: options.width, [options.side]: -options.width, zIndex: options.zIndex}).show();
    bt.css({top: p, [options.side]: p, zIndex: options.zIndex+1}).show();
        
    $(document).on('click.hamburger touchend.hamburger', function(e, d) {
        e = $(e.target);
        if(!parseInt(mobMenu[0].style[options.side]) && !(e.closest(mobMenu).length || e.is(bt) || d)) bt.trigger('click', [1]);
    });
    
    if(options.esc) $(document).on('keyup.hamburger', function(e) {
        if(e.keyCode === 27) $(this).trigger('click.hamburger', [0]);
    }); 
    
    return this.each(function () {

        bt.on('click touchend', function (e, c) {
            
            e.stopPropagation(); 
            var dir = c ? 0 : !mobMenu.hasClass(options.classActive); //направление в анимации
            
            mobMenu.toggleClass(options.classActive).animate({[options.side]: -options.width + options.width * dir}, {
                duration: options.animationSpeed, 
                step: function(pos, f) {
                    var v = f.start + f.end, 
                        d = dir ? v - v * .5 //старт анимации кнопки при открытии 
                                : v * .7; //старт анимации кнопки при закрытии
                    if($(this).data('end') && pos > d) {
                        bt.toggleClass(options.classAnimateButton); //включаем анимацию/возвращаем в начало
                        $(this).data({end: 0}) //более не реагируем на изменения
                    }
                },
                complete: function(f) {
                    $(this).data({end: 1}) //разрешаем запуск анимации
                }
            });
                
        })
    
    });
        
};
})(jQuery);


$('div.hamburger').mobileMenu({width: 400});

</script>
</body>
</html>


Пощелкайте многократно на кнопку, наблюдайте, анализируйте. Если кнопка у вас не будет перекрывать меню, находится от него в противоположной стороне (где же ей место при side: top тогда :) ), то конечно анимацию ее можно начитать и при окончании анимации меню, взгляд то при этом будет фиксирован только на одном элементе.

События на таких объектах как документ разделяйте пространством имен, указывая их и в триггерах.

Luther 13.11.2019 01:15

Цитата:

Сообщение от laimas (Сообщение 515430)
[HTML run hide height=400]<!DOCTYPE html>

Пощелкайте многократно на кнопку, наблюдайте, анализируйте. Если кнопка у вас не будет перекрывать меню, находится от него в противоположной стороне (где же ей место при side: top тогда :) ), то конечно анимацию ее можно начитать и при окончании анимации меню, взгляд то при этом будет фиксирован только на одном элементе.

События на таких объектах как документ разделяйте пространством имен, указывая их и в триггерах.

Ладно с этим я уже разберусь, последнее что хочу спросить, как теперь мне правильно построить условие, если: side: 'top' ?

laimas 13.11.2019 05:03

При фиксированном положении кнопки?

Luther 13.11.2019 10:32

Цитата:

Сообщение от laimas (Сообщение 515432)
При фиксированном положении кнопки?

Да

laimas 13.11.2019 10:50

Тогда в опциях вместо width можно определять размер size. Если side при этом будет left/right, значит это будет и ширина меню, а высота 100%, иначе это будет высота меню и ширина 100%. А математика расчета куда двигаем останется прежней, только уже не для width, а для size:

side = -size + size * dir

Luther 13.11.2019 11:01

Понял, попробую сегодня, спасибо большое за весь ваш труд, очень помогли. А следующий плагин у меня по списку - слайдер :)

Malleys 13.11.2019 11:20

laimas, а почему анимация не гладкая? (Потому что там нет анимации, а только несинхронизированное обновление стилей!)

Почему на телефоне или сенсорном экране меню как открывается, так и сразу закрывается? (Потому что touchend)

Почему 130 строк вместо пары десятков? (Потому что девиз от jQuery «Write less do more» лжив, и эта религия запрещает вам использовать современные наработки)


Часовой пояс GMT +3, время: 23:55.