多个实例上的 jQuery 插件设置

Posted

技术标签:

【中文标题】多个实例上的 jQuery 插件设置【英文标题】:jQuery Plugin settings on multiple instances 【发布时间】:2011-12-29 09:34:56 【问题描述】:

我在一个 jquery 插件的多个实例上的设置有问题。

如果我多次转换我的插件并在 onclick 绑定上提醒它们,它总是提醒相同的参数。

这是我的插件。

    /**
     * Provide user search for input elements.
     * 
     * Call this jQuery plugin on an input element to add a button
     * for searching the active directory on userdata.
     * Returned data are saved in declared target input elements.
     * 
     * Depends:
     *  jQuery UI Dialog
     *
     * @license http://www.opensource.org/licenses/mit-license.php
     * @version 1.0
     */
    ;(function ( $, window, document, undefined )

        var PROP_NAME = 'userdata';
        var IS_IFRAME = ( self !== top ) ? true : false;
        var rfuuid = new Date().getTime();

        var $jParent = ( self !== top ) ? window.parent.jQuery.noConflict() : $; 

        /**
         * Userdata manager.
         * Use the singleton instance of this class, $.userdata, to interact with the date picker.
         * Settings for (groups of) date pickers are maintained in an instance object,
         * allowing multiple different settings on the same page.
         */
        function Userdata() 
            this.regional = [];
            this.regional[''] = 
                abortText: 'Abbrechen',
                acceptText: 'Hinzufügen',
                errorTitle: 'Fehler beim Suchen',
                errorFilterText: 'Suchkriterien einschränken.',
                errorVoidText: 'Der gesuchte Kontakt konnte nicht gefunden werden.',
                errorScriptText: 'Bei der Suche ist ein Fehler aufgetreten. Falls der Fehler wieder auftritt, wenden Sie sich bitte an Ihren <a href="%s">Webadministrator.</a>',
                searchText: 'Durchsuchen',
                selectionTitle: 'Namen überprüfen',
                selectionInfoText: '"%s" kommt mehrmals vor.',
                selectionDescText: 'Wählen Sie die Adresse aus, die Sie verwenden möchten:'
            ;
            this._defaults = 
                ajaxURL: 'userdata.php',
                buttonClass: 'rf-button secondary',
                buttonContainer: '<div>',
                buttonContainerClass: 'grid_2',
                targets: 
            
            $.extend(this._defaults, this.regional['']);
        

        $.extend(Userdata.prototype, 
            /**
             * Override the default settings for all instances of the userdata plugin.
             * 
             * @param object settings The new settings to use as defaults (anonymous object).
             * @return the manager object.
             */
            setDefaults: function( settings )
                $.extend(this._defaults, settings);
                return this;
            ,

            /**
             * 
             * 
             * @param object input DOM input element.
             * @param object settings Settings for attaching new userdata functionality.
             */
            _attachDialog: function( input, settings )

                var settings = $.extend(this._defaults, settings);

                if ( !document.getElementById('rf-userdata-dialog') )
                    var inst = $jParent('<div id="rf-userdata-dialog"></div>').appendTo('body');
                    inst.dialog( autoOpen: false, close: function() $jParent('body').css('overflow', 'auto'); input.focus(); , modal: true, resizable: false ).after('<span class="ui-dialog-footer" /><span class="ui-dialog-footer-edge" />');
                
                else 
                    var inst = $('#rf-userdata-dialog');
                

                input.settings = settings;

                $(input).data('settings', settings);

                this._attachButton(input, inst);
            ,

            /**
             * 
             *
             * @param object input DOM input element.
             * @param object inst jQuery dialog instance.
             */
            _attachButton: function( input, inst )

                var manager = this,
                    $input = $(input);

                // Create search button.
                var $button = $('<button>').attr('type', 'button').html(input.settings.searchText).addClass(input.settings.buttonClass);

alert(dump($(input).data('settings'))); // WORKS FINE, everything is unique.

                /**
                 * Bind manager._searchUserdata() function on button click.
                 */
                $button.bind('click',  settings : $(input).data('settings') , function(e)

                    alert(dump(e.data.settings)); // DOES NOT WORK, always settings from last plugin call...


                    //manager._searchUserdata(input, inst);
                );

                /**
                 * Insert container with search button after input field.
                 */ 
                $input.closest('[class*="grid_"]').after(function()
                    return $(input.settings.buttonContainer).addClass(input.settings.buttonContainerClass).append($button);
                );

                /**
                 * Change default enterkey behaviour on triggering the userdata button.
                 */
                $input.bind('focusin', function()
                    $input.bind('keydown.enterOpen', function(e)
                        if ( e.keyCode === 13 )
                            $button.trigger('click');
                            return false;
                        
                    );
                );

                /**
                 * Unbind keydown event with enterOpen namespace.
                 */
                $input.bind('focusout', function()
                    $input.unbind('keydown.enterOpen');
                );
            ,

            /**
             * 
             *
             * @param object input DOM input element.
             * @param object inst jQuery dialog instance.
             */
            _searchUserdata: function( input, inst )

                var manager = this,
                    $input = $(input),
                    value = $input.val();

                /**
                 * Perform an Ajax request to get the userdata of specified user.
                 */
                $.ajax(
                    url: input.settings.ajaxURL,

                    dataType: 'json',

                    data: 'value=' + encodeURIComponent(value),

                    /**
                     * A pre-request callback function.
                     * Returning false in the beforeSend function will cancel the request.
                     */
                    beforeSend: function()
                        // If value is smaller than two characters is equal space character
                        // call showError and cancel ajax call.
                        if ( value.length <= 2 || value === ' ' || value === '' )
                            manager._showError(input.settings.errorFilterText, inst, input);
                            return false;
                        
                    ,

                    /**
                     * A function to be called if the request succeeds.
                     * 
                     * @see manager._showError() for error display.
                     * @see manager._checkName() for selecting dialog.
                     *
                     * @param object data LDAP userdata returned from server.
                     */
                    success: function( data )
                        if ( $.isEmptyObject(data) )
                            manager._showError(input.settings.errorVoidText, inst, input);
                        
                        else if ( data.error === 4 )
                            manager._showError(input.settings.errorFilterText, inst, input);
                        
                        else 
                            // If request returned more than one user, call checkName() function.
                            if ( data.length > 1 )
                                manager._checkName(input, inst, data);
                            
                            else 
                                manager._setUserdata(inst, data[0], input);
                            
                        
                    ,

                    /**
                     * A function to be called if the request fails.
                     *
                     * @see manager._showError() for more information.
                     *
                     * @param object jqXHR XMLHttpRequest object.
                     * @param string textStatus Description of occurred error.
                     * @param object errorThrown Exception object.
                     */
                    error: function( jqXHR, textStatus, errorThrown )
                        manager._showError(input.settings.errorScriptText, inst, input);
                    
                );
            ,

            /**
             * 
             *
             * @param string error Error to display.
             * @param object inst jQuery dialog instance.
             * @param object input DOM input element.
             */
            _showError: function( error, inst, input )
                inst.html('<div class="ui-dialog-container">' + error + '</div>')
                inst.dialog( title: input.settings.errorTitle, width: 400 );
                inst.dialog('open');
            ,

            /**
             * 
             *
             * @param object input DOM input element.
             * @param object inst jQuery dialog instance.
             * @param array data LDAP userdata.
             */
            _checkName: function( input, inst, data )
                var manager = this,
                    $container = $('<div>').addClass('ui-dialog-container').html('<p>' + sprintf(input.settings.selectionInfoText, $(input).val()) + '</p><p>' + input.settings.selectionDescText + '</p>'),
                    $tableWrapperOuter = $('<div>').addClass('rf-select-list-wrapper-outer'),
                    $tableWrapperInner = $('<div>').addClass('rf-select-list-wrapper-inner').css('height', 240),
                    $table = $('<table>').addClass('rf-select-list'),
                    $thead = $('<thead>').html('<tr><th>Name</th><th>Position</th><th>Telefon</th><th>Ort</th><th>E-Mail</th><th>Alias</th></tr>'),
                    $tbody = $('<tbody>');

                // Loop trough userdata and create a table row for each entry.
                for ( var i = 0, length = data.length; i < length; i++ )

                    var $row = $('<tr>').html(function()
                        this.onselectstart = function() return false; 
                        //return '<td class="user-icon">' + data[i].sn + ' ' + data[i].givenname + '</td><td>' + data[i].title + '</td><td>' + data[i].telephonenumber + '</td><td>' + data[i].location + '</td><td>' + data[i].mail + '</td><td>' + data[i].cn + '</td>';
                        return '<td class="user-icon">' + data[i].sn + ' ' + data[i].givenname + '</td><td>' + data[i].title + '</td><td>' + data[i].location + '</td><td>' + data[i].cn + '</td>';
                    );

                    $row.bind('click',  obj: data[i] , function(e)
                        var $this = $(this);

                        // Temp-save data from selection for ok-button.
                        inst.selection = e.data.obj;

                        $this.siblings().removeClass('ui-state-active');
                        $this.addClass('ui-state-active');
                    );

                    $row.bind('dblclick',  obj: data[i] , function(e)
                        inst.dialog('close');
                        manager._setUserdata(inst, e.data.obj, input);
                    );

                    $row.appendTo($tbody);
                

                // Trigger first row selection.
                $tbody.find('tr').first().trigger('click');

                // Hide scrollbar on form body to prevent scrolling problem.
                $jParent('body').css('overflow', 'hidden');

                // Create buttons and append them to a container.
                var $buttonAccept = $('<button>').addClass("rf-button primary").html(input.settings.acceptText).bind('click', function()
                    inst.dialog('close');
                    manager._setUserdata(inst, inst.selection, input);
                );

                var $buttonAbort = $('<button>').addClass("rf-button secondary").html(input.settings.abortText).bind('click', function()
                    inst.dialog('close');
                );

                // Toggle 'rf-button-hover' class on buttons hover state.
                $buttonAccept.hover(function() $buttonAccept.toggleClass('rf-button-hover'); );
                $buttonAbort.hover(function() $buttonAbort.toggleClass('rf-button-hover'); );

                var $buttonContainer = $('<div>').addClass('float-right').append($buttonAccept, $buttonAbort);

                // Append dialog html to container.
                $container.append($tableWrapperOuter.append($tableWrapperInner.append($table.append(/*$thead,*/ $tbody))), $buttonContainer);

                inst.html($container);
                inst.dialog( title: input.settings.selectionTitle, width: 800 );
                inst.dialog('open');
            ,

            /**
             * 
             *
             * @param object inst jQuery dialog instance.
             * @param array data LDAP userdata.
             */
            _setUserdata: function( inst, data, input )

                for ( var target in input.settings.targets )

                    var values = [];

                    if ( typeof(input.settings.targets[target]) === 'object' )
                        for ( var i = 0, length = input.settings.targets[target].length; i < length; i++ )
                            values.push(data[input.settings.targets[target][i]]);
                        
                    
                    else 
                        values.push(data[input.settings.targets[target]]);
                    

                    $(target).val(values.join(' '));
                
            
        );

        /**
         * Invoke the userdata functionality.
         *
         * @param object options Settings for attaching new userdata functionality.
         * @return jQuery object.
         */
        $.fn.userdata = function( options )

            // Verify an empty collection wasn't passed.
            if ( !this.length )
                return this;
            

            /**
             * Loop through each plugin object.
             */
            return this.each(function()
                $.userdata._attachDialog(this, options);
            );
        ;

        $.userdata = new Userdata();
        $.userdata.uuid = new Date().getTime();

    )( jQuery, window, document );

我在我的 html 中多次调用它:

$('#inputid_1').userdata( targets: '#targetid_1': 'cn' );
$('#inputid_2').userdata( targets: '#targetid_2': 'phone' );

现在,如果您查看 _attachButton 方法,则有两个警报。一个在点击绑定之外,一个在点击绑定内部。在点击绑定之外,设置是唯一的 foreach 插件调用。在点击绑定中,它总是提醒上次调用的设置,即使我使用 event.data 传递它们。

【问题讨论】:

【参考方案1】:

像这样扩展设置:

var settings = $.extend(settings, this._defaults); // invert the params

var settings = $.extend(, this._defaults, settings);

为什么?

$.extend() 将目标作为第一个参数。因此,您将 this._default 的属性与设置合并,而不是相反。

第二种形式()说:忽略目标,让 this._default 和设置都保持不变,只需返回一个合并的对象(希望我很清楚 ^^)。

请参阅有关 .extend() 的 jQuery 文档。

【讨论】:

不起作用,我有问题的设置在此代码部分中传递:return this.each(function() $.userdata._attachDialog(this, options); ); var settings = $.extend(, this._defaults, settings); 工作!!非常感谢。你能解释一下这个问题吗? 编辑了答案。很高兴它有帮助:-)

以上是关于多个实例上的 jQuery 插件设置的主要内容,如果未能解决你的问题,请参考以下文章

如何在单个页面上有多个 jQuery 插件实例?

JQuery SumoSelect - 页面上具有不同宽度的多个实例

在jquery中添加多个CKEditor实例

jQuery EasyUI 选项卡面板tabs使用实例精讲

同一服务器和 epmd 上的多个 RabbitMQ 实例

将 jQuery datepicker 应用于多个实例