【问题标题】:jquery plugin controllers overwrite eachotherjquery 插件控制器互相覆盖
【发布时间】:2011-08-16 16:22:25
【问题描述】:

我正在使用两个 jquery 插件(一个用于选项卡,一个用于缩略图库)来创建一个选项卡式画廊。在我尝试引入一个简单的 mvc 模式之前,一切都很顺利。现在,不知何故,无论哪个插件初始化第二个控制器都会覆盖第一个 - 导致很多“new_view.tab 未定义”,因为第一个插件试图使用第二个控制器。

这是标签的代码

( function( $ ) {

// private vars
var settings;

var Model = ( function( ) {
    var pub = { };
        current_view = null;

    pub.get_current = function( ) {
        if( current_view ) {
            return current_view;
        } else {
            return false;
        }
    }
    pub.set_current = function( new_view ) {
        if( current_view ) console.log( 'current view is ' + current_view.number );
        current_view = new_view;
        if( current_view ) console.log( 'current view is now ' +current_view.number );
    }
    return pub;
})( );

var Controller = ( function( Model ) {
    var pub = { };
        model = Model;

    pub.update = function( new_view ) {
        console.log( 'tab controller update' );

        settings.update( model.get_current_view, new_view, function( callback ) {
            if( model.get_current( ) ) {
                model.get_current( ).tab.removeClass( settings.active_class );
            }
            model.set_current( new_view );
            new_view.tab.addClass( settings.active_class );
        });
    }
    pub.close = function( new_view ) {
        settings.close_callback(  );
        pub.model.set_current( null );
    }
    console.log( 'in tabs controller');
    console.log( pub );
    return pub;
})( Model );

console.log( 'in tabs')
console.log( Controller );

var View = ( function( Controller ) {
    var pub = { };
        controller = Controller;

    pub.update = function( new_view ) {
        controller.update( new_view );
    }

    pub.close = function( new_view ) {
        controller.update( new_view );
    }

    return pub;
})( Controller );

/* private closures */
function Tab( tab, tab_content, close, number ) {
    this.tab = tab;
    this.tab_content = tab_content;
    this.close = close;
    this.number = number;
    var Tab = this;

    // listeners
    tab.bind( 'click.tab', function( evnt ) {
        console.log( 'tab' + number + ' clicked' );
        evnt.preventDefault( );
        if( Tab.tab.hasClass( settings.active_class ) ) return;
        Tab.update( Tab );
    });

    close.bind( 'click.close', function( evnt ) {
        console.log( 'tab' + number + ' close clicked' );
        evnt.preventDefault( );
        Tab.close( Tab );
    });
}
Tab.prototype = View;

//public methods
var methods = {
    init : function( options ) {

        //defaults
        var defaults = {
            'tab_class': 'tab',
            'content_class' : 'content',
            'close_class': 'close',
            'active_class': 'active',
            'animate_open': function( arg ) {
                $( arg ).show( );
            },
            'animate_close': function( arg ) {
                $( arg ).hide( );
            },
            'tab_click_callback': function( ) { },
            'close_callback': function( ){ }
        };

        return this.each( function( ) {
            //update defaults with user options
            if( options ) {
                settings = $.extend( true, { }, defaults, options );
            } else {
                settings = defaults;
            }

            var container = $( this );
            var tabs = $( '.' + settings.tab_class, container ).children( );
            var content = $( '.' + settings.content_class, container );
            var close = $( '.' + settings.close_class, container );
            var len = tabs.length;
            var i = 0;
            tabs.each( function( index ) {
                var current_tab = $( this );
                new Tab( current_tab, $( content[ index ] ), $( close[ index ] ), index + 1 );
            });
        });//end return
    },//end init
    destroy: function( ) {
        return this.each( function( ) {
            methods = null;
        });
    }
};//end methods

//add function to jquery
$.fn.id_tabs = function( method ) {
    if ( methods[method] ) {
        return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
        return methods.init.apply( this, arguments );
    } else {
        $.error( 'Method ' +  method + ' does not exist on jQuery.id_tabs' );
    }    
};

})(jQuery);

为了画廊

(function($) {

var settings;

/* private */   
var Model = ( function( ) {
    var pub = { };
        current_view = null;

    pub.get_current = function( ) {
        if( current_view ) {
            return current_view;
        } else {
            return false;
        }
    }
    pub.set_current = function( new_view ) {
        if( current_view ) console.log( 'current view is ' + current_view.number );
        current_view = new_view;
        if( current_view ) console.log( 'current view is now ' +current_view.number );
    }
    return pub;
})( );

var Controller = ( function( Model ) {
    var pub = { };
        model = Model;

    pub.update = function( view ) {
        console.log( 'gallery controller update' );
        settings.update( model.current_view, view );
        if( model.get_current( ) ) {
            model.get_current( ).thumb.removeClass( settings.active_class );
        }
        model.set_current( view );
        view.thumb.addClass( settings.active_class );
    }
    console.log( 'in gallery controller');
    console.log( pub );
    return pub;
})( Model );

console.log( 'in gallery');
console.log( Controller );

var View = ( function( Controller ) {
    var pub = { };
        controller = Controller;

    console.log( 'in gallery view' );
    console.log( controller );

    pub.update = function( new_view ) {
        console.log( 'in gallery update');
        console.log( controller );
        controller.update( new_view );
    }
    return pub;
})( Controller );

function Thumb( thumb, pic, number ) {
    this.thumb = thumb;
    this.pic = pic;
    this.number = number;
    var Thumb = this;

    this.hide = function( callback ) {

        console.log( Thumb );
        settings.animate_out( Thumb, function( ) {
            if( callback ) callback( );
        });
    }
    this.show = function( callback ) {
        console.log( Thumb );
        settings.animate_in( Thumb, function( ) { 
            if( callback ) callback( );
        });
    }
    thumb.bind( 'click.thumb', function( evnt ) {
        console.log( 'pic' + number + ' clicked' );
        evnt.preventDefault( );
        if( Thumb.thumb.hasClass( settings.active_class ) ) return;
        Thumb.update( Thumb );
    });
};
Thumb.prototype = View;

/* public */
var methods = {
    init : function( options ) {

        //defaults
        var defaults = {
            'container_id': 'content',
            'active_class' : 'active',
            'movie_class': 'movie',
            'animate_in': function( arg ) {
                $( arg ).fadeIn( );
            },
            'animate_out': function( arg, callback ) {
                $( arg ).fadeOut( );
                callback( );                    
            },
            'update' : function( current, new_view, callback) {
                if( current ) current.pic.fadeOut( );
                new_view.pic.fadeIn( );
                if( callback ) callback( );
            }
        };

        return this.each( function( ) {
            //update defaults with user options
            if( options ) {
                settings = $.extend( true, { }, defaults, options );
            } else {
                settings = defaults;
            }

            var obj = $( this );
            var li = $( 'li', obj );
            var obj_class = obj.attr( 'id' );
            var container = $( '#' + settings.container_id );
            var content = $( '.' + obj_class, container );

            li.each( function( index ) {
                var current_li = $( this );
                var current_content = $( content[ index ] );
                var src = current_li.children( ).children( ).hide( ).attr( 'src');
                var href = current_li.children( ).attr( 'href');
                current_li.css( 'background-image', 'url(' + src + ')' );
                current_content.css( 'background-image', 'url(' + href + ')' );         
                new Thumb( current_li, current_content, index );
            });
        });//end return
    },//end init
    destroy: function( ) {
        return this.each( function( ) {
            methods = null;
        });
    },
    hide_current: function( callback ) {
        console.log( 'hide_current' );
        if( Model.current_view ) {
            settings.animate_out( Model.current_view, function( ) {
                Model.current_view.thumb.removeClass( settings.active_class );
                Model.current_view = null;
                if( callback ) callback( );
            });
        } else {
            callback( );
        }
    }
};//end public methods

//add function to jquery
$.fn.id_gallery = function( method ) {
    if ( methods[method] ) {
        return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
        return methods.init.apply( this, arguments );
    } else {
        $.error( 'Method ' +  method + ' does not exist on jquery.id_gallery' );
    }    
};

})(jQuery);

我假设有些东西正在爬上作用域链,但是在每个闭包中本地定义了“控制器”,我不明白如何。

更令人沮丧的是,我创建了一个 jsFiddle 并让它在那里工作,但我终其一生都无法弄清楚它与我的生产代码之间的区别。 http://jsfiddle.net/noTxt/vWsJw/

请帮忙

【问题讨论】:

    标签: javascript jquery inheritance jquery-plugins scope


    【解决方案1】:

    我不熟悉这种为 jQuery 编写插件的风格,所以也许我遗漏了一些明显的东西,但我确实看到在两个地方都分配了一个名为“控制器”的全局变量。这可能是问题吗?

    【讨论】:

    • 完全是。昨天杀了我的人。谢谢。
    • 我是新来编写 jquery 插件和一般的 javascript。但我一直在阅读大量关于它的内容——(主要是博客等)——所以我有点把这些第一次尝试拼凑起来,从对我来说有意义的东西开始。如果您知道更好的方法,或者如何改进这种风格,我很想听听您的想法。
    • 我从未专门使用 MVC 模式编写过 jQuery 插件,但 this 看起来可能是一个很好的例子。
    猜你喜欢
    • 2014-09-07
    • 1970-01-01
    • 1970-01-01
    • 2017-02-19
    • 1970-01-01
    • 1970-01-01
    • 2014-09-09
    • 1970-01-01
    • 2012-08-23
    相关资源
    最近更新 更多