Плагин jQuery — возвращается слишком много обратных вызовов

#javascript #jquery #plugins

#javascript #jquery #Плагины

Вопрос:

Я разработал плагин jQuery, который выполняет работу по отображению и скрытию содержимого с помощью двух последовательных анимаций. Я доволен поведением плагина при использовании с одним элементом, однако, когда на странице есть два элемента с тем же именем класса, которое используется в вызове плагина, я получаю четыре возвращаемых обратных вызова. Казалось бы, плагин не совсем правильный, и я был бы признателен за любые подсказки или помощь, которые могли бы приблизить меня к завершению этого плагина.

Я стремлюсь улучшить качество своего кода и также был бы признателен за любые общие отзывы.

Рабочий пример плагина можно найти здесь: http://jsfiddle.net/nijk/sVu7h /

Код плагина выглядит следующим образом:

 (function($){
$.fn.showHide = function(method, duration, options, callback){
    //console.log(method, duration, options, callback);
    var animating = false;
    var defaults = {
        $elem: this,
        easing: "swing"
    }
    var _sh = this;
    var init = function(){ 
            _sh.method = method;
            if("show" !== _sh.method amp;amp; "hide" !== _sh.method){
                _sh.method = "show";
            }
            if( duration < 0 || (typeof(duration) == "string" amp;amp; ("slow" !== duration amp;amp; "normal" !== duration amp;amp; "fast" !== duration) ) ){
                duration = "normal";
            }
            console.log( duration, typeof(duration) );
            if(typeof(options) == "function"){
                callback = options;
                options = {};
            }
            _sh.config = $.extend({}, defaults, options);
        if(!animating){
            //return _sh.each(function(index){
                //console.log("found element number: "   (index   1));
                eval(_sh.method)();            
            //});
        }
    }
    var show = function(){
        animating = true;
        _sh.config.$elem.wrap('<div class="show-hide"/>').parent().hide();
        _sh.config.$elem.css({"opacity":0, "display":"block"});
        console.log("element height:",  _sh.config.$elem.parent().outerHeight());
        _sh.config.$elem.parent().slideDown(duration, _sh.config.easing, function(){
            _sh.config.$elem.animate({"opacity": 1}, duration, _sh.config.easing, function(){
                    console.log("show final cleanup called");
                    _sh.config.$elem.addClass("visible").unwrap();
                    $.isFunction(callback) amp;amp; callback();
                    animating = false;
            });
        });
    };
    var hide = function(){
        animating = true;
        _sh.config.$elem.wrap('<div class="show-hide"/>');
        _sh.config.$elem.animate({"opacity":0}, duration, _sh.config.easing, function(){
            _sh.config.$elem.slideUp(duration, _sh.config.easing, function(){
                console.log("hide final cleanup called");
                _sh.config.$elem.removeClass("visible").hide().unwrap();
                $.isFunction(callback) amp;amp; callback();
                animating = false;
            });
        });
    }
    init();
    return this;
}
})(jQuery);
  

@david.mchonechase: Большое вам спасибо за ваше объяснение и пример кода.

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

Рабочий код обновлен здесь: http://jsfiddle.net/nijk/sVu7h / и следующим образом:

 (function($){
  $.fn.showHide = function(method, duration, options, callback){
    var animating = false;
    var defaults = { easing: "swing" };
    var _sh = this;
    _sh.method = show;

    if("hide" === method){
        _sh.method = hide;
    }
    if( duration < 0 || (typeof(duration) == "string" amp;amp; ("slow" !== duration amp;amp; "normal" !== duration amp;amp; "fast" !== duration) ) ){
        duration = "normal";
    }
    if(typeof(options) == "function"){
        callback = options;
        options = {};
    }
    _sh.config = $.extend({}, defaults, options);

    function show(elem){
        animating = true;
        elem.wrap('<div class="show-hide"/>').parent().hide();
        elem.css({"opacity":0, "display":"block"});
        elem.parent().slideDown(duration, _sh.config.easing, function(){
            elem.animate({"opacity": 1}, duration, _sh.config.easing, function(){
                    elem.addClass("visible").unwrap();
                    $.isFunction(callback) amp;amp; callback.call(this);
                    animating = false;
            });
        });
    };
    function hide(elem){
        animating = true;
        elem.wrap('<div class="show-hide"/>');
        elem.animate({"opacity":0}, duration, _sh.config.easing, function(){
            elem.slideUp(duration, _sh.config.easing, function(){
                elem.removeClass("visible").hide().unwrap();
                    $.isFunction(callback) amp;amp; callback.call(this);
                    animating = false;
            });
        });
    };

    if(!animating){
        // loop through each element returned by jQuery selector
        return this.each(function(){
            _sh.method($(this));
        });
    }
  }
})(jQuery);
  

Ответ №1:

Проблема заключается в смешении глобальных переменных и вызова parent() . Переменная _sh.$elem содержит два элемента (по одному на результат выбора jQuery). Вызов _sh.config.$elem.parent().slideDown в функции show вызывается дважды. После завершения он запускает _sh.config.$elem.animate один раз для каждого элемента «ShowMe». Итак, parent().slideDown вызывается дважды, который затем дважды вызывает _sh.config.$elem.animate .

Обычно я стараюсь избегать глобальных переменных в плагинах jQuery для таких функций, как ваше отображение и скрытие, но критическая часть — это элементы. (Однако анимирующая глобальная переменная имеет смысл.)

Я думаю, что что-то подобное сработает:

 (function($){
    $.fn.showHide = function(method, duration, options, callback){
        //console.log(method, duration, options, callback);
        var animating = false;
        var defaults = {
            //$elem: this,
            easing: "swing"
        }
        var _sh = this;
        var init = function(){ 
            var methodFn = show; // reference actual function instead of string, since eval is evil (usually)
            if("hide" === method){
                methodFn = hide;
            }
            if( duration < 0 || (typeof(duration) == "string" amp;amp; ("slow" !== duration amp;amp; "normal" !== duration amp;amp; "fast" !== duration) ) ){
                duration = "normal";
            }
            console.log( duration, typeof(duration) );
            if(typeof(options) == "function"){
                callback = options;
                options = {};
            }
            _sh.config = $.extend({}, defaults, options);
            if(!animating){
                // loop through each element returned by jQuery selector
                _sh.each(function(){ 
                    methodFn($(this)); // pass the single element to the show or hide functions
                });
            }
        }
        var show = function(elem){
            animating = true;
            elem.wrap('<div class="show-hide"/>').parent().hide();
            elem.css({"opacity":0, "display":"block"});
            console.log("element height:",  elem.parent().outerHeight());
            elem.parent().slideDown(duration, _sh.config.easing, function(){
                elem.animate({"opacity": 1}, duration, _sh.config.easing, function(){
                        console.log("show final cleanup called");
                        elem.addClass("visible").unwrap();
                        $.isFunction(callback) amp;amp; callback();
                        animating = false;
                });
            });
        };
        var hide = function(elem){
            animating = true;
            elem.wrap('<div class="show-hide"/>');
            elem.animate({"opacity":0}, duration, _sh.config.easing, function(){
                elem.slideUp(duration, _sh.config.easing, function(){
                    console.log("hide final cleanup called");
                    elem.removeClass("visible").hide().unwrap();
                    $.isFunction(callback) amp;amp; callback();
                    animating = false;
                });
            });
        }

        init();
        return this;
    }
})(jQuery);