Jquery插件 - 如何在init函数外访问var

时间:2012-07-29 18:21:16

标签: jquery plugins

好吧,我已经在这里阅读了几个主题,以及如何在init函数之外访问var,但因为我是javascript的新手并没有弄清楚如何去做。我已经感谢你的时间了。

我正在编写我的第一个jquery插件 - 一个可以排序,搜索文本,高亮行等等的表 - 我正面临一个问题。

在'settings'块,有一个选项i18N:{'x','y'} - 在init方法中我可以像'options.i18N.search'一样访问它。但是在方法* get_table_body *中,在jqxhr.complete的开头(/ *插入选项来管理表数据* /),我无法访问'options.i18N.iSelected'。好吧,我知道所有关于范围,但我可以解决的唯一方法是使'options = $ .extend(settings,option)' - 使其成为全局。

所以,我问你:在这段代码中我可以访问它的另一种方式是什么 - 没有使“选项”全局化?我已经尝试了$ .yTable.options ......以及其他一些组合但没有成功 - 这是我所期待的,因为我不知道自己在做什么。

(function($){
var methods = {
    init : function( option ) { 
        /* Main settings */
        var settings = {
            tableClass:'yTable', /* the class of the table */
            dataFile: 'yTable.php', /* the file that fetch data in JSON format */
            dbTable:'yTableDB', /* the database name that holds the data */
            tColumns: [''], /* an array with the header titles of the table */
            dbTColumns: [''], /* an array with the table name in database */
            dbHColor: '#ded', /* Hover color of rows */
            dbCColor: '#ded', /* Color of column when sorting */
            tStrLen: 1, /* The minimum string length to search */
            insertBottonTh: false, /* Can insert header on bottom? */
            hasFunctions: false, /* Without extra functions */
            canDeleteRow : false, /* Cannot delete single row */
            canDeleteRows: false, /* Cannot delete rows */
            canSort: false, /* Cannot sort table columns */
            /* Options to translate returned text messages */
            i18N: {
                search : 'Buscar',
                confirm : 'Confirmar',
                confirmAll : 'Deseja realmente apagar os registros selecionados'
                        +'? Esta ação não poderá ser desfeita.',
                confirmDel : 'Por favor, confirme a remoção deste registro',
                iSelected : 'Com selecionados',
                noneSelected : 'Para utilizar esta função é necessário que selecione pelo menos um registro.',
                remove : 'Excluir'
            }
        };
        /* Simplify options name */
        var options = $.extend(settings, option);
        var tClass = options.tableClass;
        var tFile = options.dataFile;
        var tName = options.dbTable;
        var tColumns = options.tColumns;
        var dbColumns = options.dbTColumns;
        var tHoverColor = options.dbHColor;
        var tCColor = options.dbCColor;
        var tSLength = options.tStrLen;
        var tThBottom = options.insertBottonTh;
        var tFunctions = options.hasFunctions;
        var tcDeleteRow = options.canDeleteRow;
        var tcDelete = options.canDeleteRows; 
        var tcSort = options.canSort;
        var yTable='table.'+tClass;

        return this.each(function(){
            var obj = $(this);
            var n=tColumns.length;
            if($.isArray(tColumns)&&n>0){
                var th=false;
                var sort_img=false;
                if(tcSort)
                    sort_img='<div class="sort-img"></div>';
                else sort_img='';
                if(tFunctions&&tcDelete)
                    th+='<th width="18"><input type="checkbox" class="check_all" /></th>';
                /* Create the header */
                $.each(tColumns, function(k,v){
                    th+='<th title="'+dbColumns[k]+'" class="th'+k+'">'+v+sort_img+'</th>';
                });
            }
            /* Append table to object */
            var table='<div id="yTableSearch">'+options.i18N.search+': <input type="text" class="yTSearch" /></div>'
            +'<div style="clear:both;"></div><div class="yTableFunctions"></div>'
            +'<table class="'+tClass+'"></table>';
            obj.append(table);
            /* Append delete single row if requested */
            if(tcDeleteRow)
                th+='<th></th>';
            /* Append the headers to table */
            $(yTable).append('<thead>'+th+'</thead>');
            /* If bottom is requested, insert it - its content is the same of thead */
            if(tThBottom)
                $(yTable).append('<tfoot>'+th+'</tfoot>');
            /* Load the body of the table(tbody) */
            methods.get_table_body(tClass, tFile, tName,dbColumns,
                tFunctions, tcDeleteRow, tHoverColor, tCColor, tSLength, tThBottom, tcDelete, yTable);
        });
    },
    get_table_body: function(tClass, tFile, tName, dbColumns,
        tFunctions, tcDeleteRow, tHoverColor, tCColor, tSLength, tThBottom,
        tcDelete, yTable, tCol, qStr){
        $(yTable+' input.check_all').prop('checked',false);
        /* Parameters to receive(order) */
        /* tClass, tFile, tName, dbColumns, tFunctions, tcDeleteRow,tHoverColor,
         * tThBottom, tCColor, tSLength, tcDelete, yTable[, tCol][, qStr] */
        /*****************************************/
        if(!tCol) tCol=':';
        var Cols = tCol.split(':');
        var rCol = $.trim(Cols[0]);
        var sCol = $.trim(Cols[1]);
        var colName = $.inArray(sCol, dbColumns);
        var qCol='';
        var sType='';
        var qByName='';
        var asort=Array(2);
        if(typeof(sCol)=='string' && colName>=0){
            var hObj = $(yTable+' thead th[title='+sCol+']')
            .prop('class');
            var tc=hObj
            .match(/th{1}[^a-z]/);
            var cObj = $(yTable+' th.'+tc);
            var sortImg = $(yTable+' th.'+tc+' div.sort-img');
            var order = hObj.match(/ASC|DESC/);
            if(cObj.hasClass('DESC')&&rCol=='sstring'){
                cObj.removeClass('DESC').addClass('ASC');
                sortImg.css('background-position', '-32px 0');
                asort=[sCol, 'ASC'];
            } else if(cObj.hasClass('ASC')&&rCol=='sstring'){
                cObj.removeClass('ASC').addClass('DESC');
                sortImg.css('background-position', '-48px 0');
                asort=[sCol, 'DESC'];
            } else if(rCol=='qstring'&&order!=null){
                asort=[sCol, $.trim(order)];
            } else {
                $(yTable+' th').removeClass('ASC').removeClass('DESC');
                $(yTable+' th div.sort-img').css('background-position', '0 0');
                cObj.addClass('ASC');
                sortImg.css('background-position', '-32px 0');
                asort=[sCol, 'ASC'];
            }
        }
        if($.isArray(asort)){
            var qCol = asort[0];
            var sType = asort[1];
        }
        if(qStr){
            if(qStr.length>=tSLength && typeof(sCol)=='string'){
                qByName = qStr;
            }
        }

        /*****************************************/
        /* Get data in JSON format */
        var jqxhr = $.get(tFile, {
            'table':tName,
            'cname': dbColumns.join(','),
            'column' : qCol,
            'sort' : sType,
            'sname' : qByName,
            'slength' : tSLength
        },
        function(){
            /* Remove tbody to append a new one */
            $('table.'+tClass+' tbody').remove();
        }).success(function(data){
            /* Mount tbody content */
            if(data!='norow'){
                var rows = $.parseJSON(data);
                var i=0;
                var tbody=false;
                tbody+='<tbody>';
                var tr = false;                
                $.each(rows,function(){
                    var objf = rows['tr'+i];
                    tr+='<tr id="tr'+i+'">'
                    if(tFunctions&&tcDelete)
                        tr+='<td><input type="checkbox" name="tr'+i+'" class="check" /></td>';
                    $.each(objf, function(v){
                        if(v!=='id'){
                            tr+='<td>'+objf[v]+'</td>';
                        }
                    });
                    if(tcDeleteRow)
                        tr+='<td><div class="delete_row" id="r'+objf['id']+'"></div></td>';
                    tr+='</tr>';
                    i++;
                });
            } else {
                var ncolumns = dbColumns.length;
                if(tFunctions&&tcDelete) {
                    ncolumns++;
                }
                if(tcDeleteRow)
                    ncolumns++;
                tr = '<tr><td colspan="'+ncolumns+'">...</td></tr>';
            }
            tbody+=tr+'</tbody>';
            $(yTable).append(tbody);
        }).error(function(){
            alert('Fail');
        });
        jqxhr.complete(function(){
            /* Permit select/deselect all inputs */
            $(yTable+' input.check_all').click(function(){
                var val = this.checked;
                $('.check_all').prop('checked',this.checked);
                var input = $(yTable+' tbody input[type=checkbox]');
                input.prop('checked', val);
                if(input.prop('checked')==true)
                    input.closest('tr').addClass('ySelected');
                else input.closest('tr').removeClass('ySelected');
            });
            /* Insert options to manage table data */
            if(tFunctions&&tcDelete){
                if($('div.yTableFunctions').children().length==0){
                    $('div.yTableFunctions').append(options.i18N.iSelected+': <select class="yTableOperations">'
                        +'<option class="none"> -- </option></select>'
                        +' <input type="button" value="'+options.i18N.confirm+'" name="bConfirm" />');
                    $('div.yTableFunctions').clone().insertAfter(yTable);
                }
                /* If can delete by selection */
                if(tFunctions&&tcDelete){
                    if($('div.yTableFunctions select.yTableOperations option.delete').length==0){
                        methods.delete_selected(yTable);
                    }
                }                    
            }
            /* If requested to delete single row */
            if(tcDeleteRow){
                methods.delete_single_row(tClass);
            }
            /* Stripe table rows */
            methods.stripe_table(tClass);
            /* Select row on click */
            methods.select_on_click(tClass);
            /* First verify what column will be hightlighted ... */
            var i=parseInt($(yTable+' thead th[title='+sCol+']').index(),10)+1;
            /* ... now highlight coresponding column */
            $(yTable+' tr td:nth-child('+i+')').css({
                'background-color': tCColor
            });
            /* ... now sort table */
            methods.sort_table($(yTable+' div.sort-img'),yTable,tClass,
                tFile, tName,dbColumns, tFunctions, tcDeleteRow, tHoverColor, tCColor, tSLength, tThBottom, tcDelete);
            /* Highlight on mouse over */
            methods.highlight_on_hover(tClass, tHoverColor,tCColor);
            methods.search_string(tClass, tFile, tName, dbColumns, tFunctions, tcDeleteRow,
                tHoverColor, tCColor, tSLength, tThBottom, tcDelete, yTable);
        });
    },
    stripe_table: function(c){
        $('table.'+c+' tbody tr').removeClass('stripes');
        $('table.'+c+' tr:nth-child(even)').addClass('stripes');
    },
    highlight_over_highlighted_cell: function(trId,c,h){
        $('table.'+c+' thead th').each(function(){
            if($(this).hasClass('ASC')||$(this).hasClass('DESC'))
                var thId = true;
            if(thId){
                var tHClass=$(this).prop('class').match(/th{1}[^a-z]/);
                /* First verify what column will be hightlighted ... */
                var i=parseInt($('table.'+c+' thead th.'+tHClass).index(),10)+1;
                /* ... now highlight coresponding cell */
                $('table.'+c+' tr#'+trId+' td:nth-child('+i+')').css({
                    'background-color': h
                });
            }
        });
    },
    highlight_on_hover: function(c,h,rh){
        var thisTr = $('table.'+c+' tbody tr');
        thisTr.mouseover(function(){
            /* Tr id */
            var thisTrId=$(this).prop('id');
            $(this).css({
                'background-color':h
            });
            methods.highlight_over_highlighted_cell(thisTrId,c,h);
        }).mouseout(function(){
            $(this).css('background-color','');
            /* Tr id */
            var thisTrId=$(this).prop('id');
            methods.highlight_over_highlighted_cell(thisTrId,c,rh);
        });
    },
    select_on_click:function(c){
        $('table.'+c+' tbody tr').click(function(){
            var $this = $(this);
            var input = $this.children('td').children('input');
            /* Select/desselect row input */
            input.prop('checked', input.prop('checked')?false:true);
            /* Toggle highlight*/
            if(input.prop('checked')==true)
                $this.addClass('ySelected');
            else $this.removeClass('ySelected');
        });
    },
    delete_single_row : function(c){
        $('div.delete_row').click(function(){
            $(this).closest('tr').addClass('highlight_row');
            //                var rId = this.id.replace(/r/,'');
            if(confirm(options.i18N.confirmDel)){
                //                    $.get('delete_row.php',{
                //                        id:rId
                //                    },
                //                    function(data){
                //                        alert(c);
                //                    });
                $(this).closest('tr').hide();
            }else{
                $(this).closest('tr').removeClass('highlight_row');
            }
        });
    },
    delete_selected: function(c){
        var oper = $('select.yTableOperations');
        oper.append('<option class="delete" value="delete">'+options.i18N.remove+'</option>');
        oper.change(function(){
            var opt = $(this).val();
            oper.each(function(){
                $(this).prop('value',opt); 
            });
        });
        $('.yTableFunctions input[type=button]').click(function(){
            if(this.value === options.i18N.confirm && oper.val()==='delete'){
                var regs=[];
                var i=0;
                $(c+' input[type=checkbox].check')
                .each(function(){
                    if(this.checked){
                        regs[i] = this.name;
                        $('tr#'+regs[i]).addClass('highlight_row');/* Highlight marked rows */
                        i++;
                    }
                });
                if(regs.length>0){
                    var rows=[];
                    if(confirm(
                        options.i18N.confirmAll
                        )){
                        $.each(regs, function(k,v){/* Loop through each row */
                            rows[k]='"'+k+'":"'+v+'"';
                            $('tr#'+v).addClass('highlight_del')/* Highlight to delete */
                            .hide('slow') /* Give some time to hide */
                            .remove();/* Remove row */
                        });
                        rows = '{'+rows.join()+'}'; 
                        $(c+' input[type=checkbox].check_all')
                        .prop('checked',false);
                    } else {
                        $('tr').removeClass('highlight_row');
                    }
                }else{
                    alert(options.i18N.noneSelected);
                }
            }
        });            
    },
    sort_table : function(ce,yTable, tClass, tFile, tName,dbColumns,
        tFunctions, tcDeleteRow, tHoverColor, tCColor, tSLength, tThBottom, tcDelete){
        $(ce).unbind('click').click(function(e){
            e.stopPropagation();
            var tCol='sstring:'+$(this).closest('th').prop('title');
            var qStr=$('#yTableSearch input.yTSearch').val();
            /* Get data sorted */
            methods.get_table_body(tClass, tFile, tName,dbColumns, tFunctions,
                tcDeleteRow, tHoverColor, tCColor, tSLength, tThBottom, tcDelete, yTable, tCol, qStr);
        });
    },
    search_string : function(tClass, tFile, tName,dbColumns, tFunctions, tcDeleteRow,
        tHoverColor, tCColor, tSLength, tThBottom, tcDelete, yTable){
        $('#statistics').unbind('keyup').on('keyup', '#yTableSearch input.yTSearch',
            function(e){
                e.stopImmediatePropagation();
                var qStr=null;
                if(this.value.length>=tSLength
                    &&((e.keyCode>46&&e.keyCode<91)
                        ||(e.keyCode>95&&e.keyCode<106))
                    ||e.keyCode==8||e.keyCode==46
                    ){
                    qStr = this.value;
                    var noSort = 'qstring:';
                    $(yTable+' thead tr th').each(function(e){
                        if($(this).hasClass('ASC')|| $(this).hasClass('DESC')){
                            noSort += $(this).prop('title');
                        }
                    })
                    methods.get_table_body(tClass, tFile, tName,dbColumns, tFunctions,
                        tcDeleteRow, tHoverColor, tCColor, tSLength, tThBottom, tcDelete, yTable, noSort, qStr);
                }                        
            });
    }
};
$.fn.yTable=function(method){
    /* Method calling logic */
    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( 'Desculpe-me, mas o método "' +  method + '" não existe no plugin yTable!' );
    }    
};
})(jQuery);

有效!感谢您抽出宝贵时间帮助我解决这个问题。我将把解决方案放在下面,因为其他人可能会遇到同样的问题。遵循@ Beetroot-Beetroot指令,我对这个问题感到不安。

修改很少......见下文

方法初始化:

首先,我从设置中撤回i18N块并将其放入返回this.each()

    init : function( options ) { 
  // codes ...
return this.each(function(){
            var obj = $(this);
            var data = obj.data('yTable');
            data ={
                /* Options to translate returned text messages */
                i18N: {
                    target : obj,
                    search : 'Buscar',
                    confirm : 'Confirmar',
                    confirmAll : 'Deseja realmente apagar os registros selecionados'
                    +'? Esta ação não poderá ser desfeita.',
                    confirmDel : 'Por favor, confirme a remoção deste registro',
                    iSelected : 'Com selecionados',
                    noneSelected : 'Para utilizar esta função é necessário que selecione pelo menos um registro.',
                    remove : 'Excluir',
                    showReg : 'Mostar ',
                    showRegEnd : ' por página',
                    failDbQuery : 'Erro! Não foi possível buscar dados na base de dados. Por favor, verifique sua configuração.'
                }

            }
            obj.data('yTable', data);

// var options = $ .extend(data,options);                 var yOptionLang = obj.data('yTable');

现在我可以通过 yOptionLang.i18N.showReg 访问 init 中的i18N。因为我需要将i18N传递给方法 get_table_body ,所以我通过执行* methods.get_table_body(tClass ... yTable).apply(this)*。

当我从 get_table_body 调用其他方法时,我将其重写为......

get_table_body:function(tClass,...nReg){
        return $.each(function(){
            var yOption = $(this).data('yTable');
            // code here ...

我可以通过 yOption.i18N.iSelected 来访问i18N ...现在,在这个方法中我必须将i18N传递给其他方法,所以我这样做......

/* If requested to delete single row */
                if(tcDeleteRow){
                    methods.delete_single_row(tClass).apply(yOption);
                }

...在这种情况下,方法 delete_single_row 将以...开头

delete_selected:function(c){
        return $.each(function(){
            var yOption = this;
            // code here...

...我可以通过 yOption.i18N.remove

访问i18N

其他棘手的问题是,我需要从其他方法调用 get_table_body ,所以我必须在这些方法中添加 .apply(this),如下所示对表格进行排序。

sort_table:function(ce,yTable, tClass, tFile, tName,dbColumns,
        tFunctions, tcDeleteRow, tHoverColor, tCColor, tSLength, tThBottom, tcDelete){
        $(ce).unbind('click').click(function(e){
            e.stopPropagation();
            var tCol='sstring:'+$(this).closest('th').prop('title');
            var qStr=$('#yTableSearch input.yTSearch').val();
            var nReg = $('div#yTableRegs select#yTableRegSelect').prop('value');
            /* Get data sorted */
            methods.get_table_body(tClass, tFile, tName,dbColumns, tFunctions,
                tcDeleteRow, tHoverColor, tCColor, tSLength, tThBottom, tcDelete, yTable, tCol, qStr, nReg).apply(this);
        });
    }

嗯,现在通过这些修改我可以将变量从方法传递给方法 - 谢谢你的帮助!

1 个答案:

答案 0 :(得分:1)

问题

这种模式非常聪明,但很难渗透。

要理解的主要内容是:

  • 在最外层范围内声明的任何变量(在var methods = ...之前)将是对插件本身的特定(和私有),而不是对它的任何特定调用
  • 在任何方法中声明的任何变量对于该方法的每个特定调用都是特定的(和私有的),并且不能直接用于任何其他方法。 init方法与这方面的任何其他方法没有什么不同。

因此,没有明显的方法在方法之间共享变量。

DOM救援

幸运的是,jQuery使得在DOM中存储数据变得简单,而这正是这里所必需的。

以下是init的模板,基于教程 here

init : function(options) {
    return this.each(function(index){
        var $this = $(this);
        var data = $this.data(pluginName);
        // If the plugin hasn't been initialized yet
        if (!data) {
            var settings = {
                //default settings here
            };
            if(options) { $.extend(settings, options); }
            data = {
                target : $this,
                //other data properties here
            }
            $this.data(pluginName, data);
            //do other init stuff here
            //create further data properties as required
        }
    });
},

因此,在init中建立的数据可用于其他方法:

myMethod : function() {
    return this.each(function(index){
        var $this = $(this);
        var data = $this.data(pluginName);
        //do other method stuff here
        //access data properties as required
    });
},