Javascript-форум (https://javascript.ru/forum/)
-   Элементы интерфейса (https://javascript.ru/forum/dom-window/)
-   -   Не доступна переменная vars в методе destroy (https://javascript.ru/forum/dom-window/59948-ne-dostupna-peremennaya-vars-v-metode-destroy.html)

Алексей Петрович 05.12.2015 00:52

Я доделал свой плагин, прошу оценки, советов по модернизации.
/* ------------------------------------------------------------------

Плагин выпалывающего меню .DropDown()

Опции:
  button_element: 'a',             Ссылка на кнопку для открытия меню
  menu_element: 'ul',              Ссылка на дочернее меню
  event: 'hover',                  Событие для открытия меню hover или click
  animate: 'slide',                Название анимации none, fade или slide
  animate_duraction: 500,          Скорость анимации в миллисекундах
  open_class: 'open',              Класс для открытого меню
  wrap_class: 'dropdown',          Класс обёртки меню
  button_class: 'dropdown-button', Класс кнопки меню
  menu_class: 'dropdown-menu'      Класс по меню

События:
  render.dropdown                  Визуализация, добавление нужных стилей, классов
  open.dropdown                    Открытие меню
  close.dropdown                   Закрытие меню

Методы:
  init                             Вызывается по умолчанию, инициализирует плагин
  update                           Обновляет настройки плагина
  destroy                          Удаляет работу плагина

Пример:
  HTML:
    <div class="menu">
      <a href="#">Открыть</a>
      <ul>
        <li><a href="#">Пункт 1</a></li>
        <li><a href="#">Пункт 2</a></li>
        <li><a href="#">Пункт 3</a></li>
      </ul>
    </div>

  JS:
    $(".menu").DropDown();

P.S. Это меню изначально придумано для WordPress, но подойдёт практически для
чего угодно так как оно требует минимальной вёрстки, и очень хорошо настраивается.
  
------------------------------------------------------------------ */

;(function ($) {
    "use strict";

    $.fn.DropDown = function(method, options) {
        var defaults, methods;

        // Если не указаны пользовательские настройки
        if(options === undefined) {
            options = {};
            // Если пользователь не указал метод но указал свои настройки
            if(typeof method === 'object') {
                options = method;
            }
        }

        // Стандартные настройка
        defaults = {
            button_element: 'a',
            menu_element: 'ul',
            menu_width: '200px',
            event: 'hover',
            animate: 'slide',
            animate_duraction: 500,
            open_class: 'open',
            wrap_class: 'dropdown',
            button_class: 'dropdown-button',
            menu_class: 'dropdown-menu'
        };

        // Методы плагина
        methods = {
            init: function () {
                // Цикл по всем элементам
                return this.each(function () {
                    var $wrap, $button, $menu, data, vars;

                    // Обёртка меню
                    $wrap = $(this);

                    // Получение data настроек
                    data = $wrap.data('dropdown');
                    data = !data ? {} : data;

                    // Выходим если инициализация уже была
                    if (data.init) return false;
                    data.init = true;

                    // Обледенением стандартные и пользовательские настройки
                    vars = $.extend({}, defaults, data, options);
                    $wrap.data('dropdown', vars);

                    // Элементы меню
                    $button = $wrap.children(vars.button_element);
                    $menu = $wrap.children(vars.menu_element);

                    // Визуализация
                    $wrap
                        .addClass(vars.wrap_class)
                        .trigger('render.dropdown');

                    $menu
                        .addClass(vars.menu_class)
                        .css('display', 'none');

                    $button.addClass(vars.button_class);

                    // Открытие закрытие меню
                    switch (vars.event) {
                        // При наведении
                        case 'hover':
                            $wrap.on('mouseenter.dropdown', function () {
                                open($(this));
                            }).on('mouseleave.dropdown', function () {
                                close($(this));
                            });
                            break;
                        // При клике
                        default:
                            $button.on('click.dropdown', function () {
                                var $this_wrap = $(this).parent($wrap);

                                if($this_wrap.hasClass(vars.open_class)) {
                                    close($this_wrap);
                                } else {
                                    open($this_wrap);
                                }
                            });
                            // При клике вне меню закрываем его
                            $(document).on('click.dropdown', function (event) {
                                if ($(event.target).closest($wrap).length) return;
                                close($wrap);
                                event.stopPropagation();
                            });
                            break;
                    }
                });
            },
            update: function() {
                methods.destroy.apply(this, options);
                methods.init.apply(this, options);
            },
            destroy: function() {
                var $wrap, vars, $button, $menu;

                // Обёртка
                $wrap = $(this);

                // Настройки
                vars = $wrap.data('dropdown');

                // Элементы меню
                $button = $wrap.children(vars.button_element);
                $menu = $wrap.children(vars.menu_element);

                // Если плагин не инициализирован то выходим
                if (!vars) return false;

                // Удаляем классы\стили
                $wrap.removeClass(vars.wrap_class);
                $menu.removeClass(vars.menu_class)
                     .css('display', '');
                $button.removeClass(vars.button_class);

                // Отменяем события
                $wrap.off('.dropdown');
                $button.off('.dropdown');
                $(document).off('.dropdown');

                // Удаляю настройки плагина
                $wrap.data('dropdown', '');
            }
        };

        // Вызов методов
        // Если закрашиваемый метод существует, вызываем его
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        // Если передан объект или ничего, вызываем метод init
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        // Если метода не существует, выдаём ошибку
        } else {
            $.error('Метод с именем ' +  method + ' не существует для jQuery.dropdown');
        }

        // Функция открытия меню
        function open(element) {
            var vars = element.data('dropdown');

            element
                .addClass(vars.open_class)
                .trigger('open.dropdown');

            // Анимация
            switch (vars.animate) {
                case 'slide':
                    element
                        .children(vars.menu_element)
                        .stop(true, true)
                        .slideDown(vars.animate_duraction)
                        .css('display', 'block');
                    break;
                case 'fade':
                    element
                        .children(vars.menu_element)
                        .stop(true, true)
                        .fadeIn(vars.animate_duraction)
                        .css('display', 'block');
                    break;
                default:
                    element
                        .children(vars.menu_element)
                        .css('display', 'block');
                    break;
            }
        }

        // Функция закрытия меню
        function close(element) {
            var vars = element.data('dropdown');

            element
                .removeClass(vars.open_class)
                .trigger('close.dropdown');

            // Анимация
            switch (vars.animate) {
                case 'slide':
                    element
                        .children(vars.menu_element)
                        .stop(true, true)
                        .slideUp(vars.animate_duraction);
                    break;
                case 'fade':
                    element
                        .children(vars.menu_element)
                        .stop(true, true)
                        .fadeOut(vars.animate_duraction);
                    break;
                default:
                    element
                        .children(vars.menu_element)
                        .css('display', 'none');
                    break;
            }
        }
    };
}(jQuery));

рони 05.12.2015 00:57

Алексей Петрович,
строка 160
// Если плагин не инициализирован то выходим 
                if (!vars||(vars &&!vars.init)) return false;

Алексей Петрович 05.12.2015 01:48

А что это значит?

рони 05.12.2015 02:04

Цитата:

Сообщение от Алексей Петрович
А что это значит?

data-dropdown отсутствует или есть но непроинициализирована

Алексей Петрович 05.12.2015 02:17

Понятно.

Тут мне пришло в голову сделать, то что не когда не делал))) Управление с клавиатуры.
32 - Пробел
13 - Интер

И тут возникла проблема, я хочу что бы меню открывалось по пробелу или интеру.

$button.on('keypress.dropdown', [32, 13], function () {
   alert('пробел или интер');
});


Но почему то работает только пробел(((

рони 05.12.2015 02:30

Алексей Петрович,
может keyup или keydown тогда?

Алексей Петрович 05.12.2015 02:39

А какая по сути разница?
keypress - Нажатие клавиши, о налог keyup, полное нажатие клавиши
keyup - Поднятие клавиши, когда отпустил клавишу
keydown - Опускание клавиши, нажал но не отпустил

Вот и вся разница, как будто они как то кардинально отличаются в своей работе.

Я тут уже подумал, а как юзер поймет что что-то надо нажимать.
Думаю по фокусу открывать меню, это просто. А вот с закрытием проблема, это как то надо отслеживать что нет дочерних элементов в фокусе.

Алексей Петрович 05.12.2015 04:55

Пришла в голову мысль:
По фокусу открывать меню.
Отслеживать нажатие tab в любом дочернем элементе.
Если tab нажат на последнем focus'абельном элементе, то закрывать меню.

Вообще возможно как то получить список focus'абельных элементов, что бы из него вытащить последний для условия?

На такую мысль я набрел, после того когда вспомнил что плагин не имеет почти не каких привязанностей от вёрстки (Но может чуть-чуть) и следовательно общепринятое управление клавиатурой (пробел открыть, верх низ переключаться по меню) не возможно.
Вот пример: http://jsfiddle.net/gasu9be5/7/, последний пункт меню я туда вообще поиск засунул.

рони 05.12.2015 10:13

Алексей Петрович,
:victory:

Алексей Петрович 05.12.2015 12:09

:(


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