分类目录归档:jquery

jQuery 插件开发

最近开发一个前端图表模块,使用jQuery和Highchart开发,然后提供给其他人用,发现调用时要写太多代码,能不能一行搞定。之前开发过几个jQuery扩展,于是也做成jQuery扩展,调用时类似$(‘#chart’).warning(options)这样子,简洁了不少。
jQuery扩展有两种,一种是$(‘#element’).func(),使用了选择器,定义是:

(function($) {
    //Plugin fucntions
    $.fn.PluginName = function(options) {
        // Plugin initial
    }
})(jQuery);

将PluginName这个插件注册到$.fn上面,另外一种$.func(options),不绑定元素的,定义是:

(function($) {
    //Plugin fucntions
    $.PluginName = function(options) {
        // Plugin initial
    }
})(jQuery);

将PluginName直接挂在了$对象下面。
jQeury.extend方法可以用来扩展对象,也可以用来增加插件:

//$('#el').PluginName方式
jQuery.fn.extend({
    PluginName: function(options) {
        // Plugin initial
    }
});


//对原有的PluginName插件再进一步扩展,也可用于升级插件而不修改原有代码
jQuery.fn.PluginName.extend({
    doSomething: function(options) {
        // Plugin extend
    }
});

jQuery.extend(true, jQuery.fn.PluginName.prototype, {});

//$.PluginName方式
jQuery.extend({
    PluginName: function(options) {
        // Plugin initial
    }
});

jQuery.PluginName.extend({
    doSomething: function(options) {
        // Plugin extend
    }
});

以下是一个简单的jQuery插件模板,来自这里

/*
 *  Project: 
 *  Description: 
 *  Author: 
 *  License: 
 */

// the semi-colon before function invocation is a safety net against concatenated
// scripts and/or other plugins which may not be closed properly.
;(function ( $, window, document, undefined ) {

    // undefined is used here as the undefined global variable in ECMAScript 3 is
    // mutable (ie. it can be changed by someone else). undefined isn't really being
    // passed in so we can ensure the value of it is truly undefined. In ES5, undefined
    // can no longer be modified.

    // window is passed through as local variable rather than global
    // as this (slightly) quickens the resolution process and can be more efficiently
    // minified (especially when both are regularly referenced in your plugin).

    // Create the defaults once
    var pluginName = 'defaultPluginName',
        defaults = {
            propertyName: "value"
        };

    // The actual plugin constructor
    function Plugin( element, options ) {
        this.element = element;

        // jQuery has an extend method which merges the contents of two or
        // more objects, storing the result in the first object. The first object
        // is generally empty as we don't want to alter the default options for
        // future instances of the plugin
        this.options = $.extend( {}, defaults, options) ;

        this._defaults = defaults;
        this._name = pluginName;

        this.init();
    }

    Plugin.prototype.init = function () {
        // Place initialization logic here
        // You already have access to the DOM element and the options via the instance,
        // e.g., this.element and this.options
    };

    // You don't need to change something below:
    // A really lightweight plugin wrapper around the constructor,
    // preventing against multiple instantiations and allowing any
    // public function (ie. a function whose name doesn't start
    // with an underscore) to be called via the jQuery plugin,
    // e.g. $(element).defaultPluginName('functionName', arg1, arg2)
    $.fn[pluginName] = function ( options ) {
        var args = arguments;

        // Is the first parameter an object (options), or was omitted,
        // instantiate a new instance of the plugin.
        if (options === undefined || typeof options === 'object') {
            return this.each(function () {

                // Only allow the plugin to be instantiated once,
                // so we check that the element has no plugin instantiation yet
                if (!$.data(this, 'plugin_' + pluginName)) {

                    // if it has no instance, create a new one,
                    // pass options to our plugin constructor,
                    // and store the plugin instance
                    // in the elements jQuery data object.
                    $.data(this, 'plugin_' + pluginName, new Plugin( this, options ));
                }
            });

        // If the first parameter is a string and it doesn't start
        // with an underscore or "contains" the `init`-function,
        // treat this as a call to a public method.
        } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {

            // Cache the method call
            // to make it possible
            // to return a value
            var returns;

            this.each(function () {
                var instance = $.data(this, 'plugin_' + pluginName);

                // Tests that there's already a plugin-instance
                // and checks that the requested public method exists
                if (instance instanceof Plugin && typeof instance[options] === 'function') {

                    // Call the method of our plugin instance,
                    // and pass it the supplied arguments.
                    returns = instance[options].apply( instance, Array.prototype.slice.call( args, 1 ) );
                }

                // Allow instances to be destroyed via the 'destroy' method
                if (options === 'destroy') {
                  $.data(this, 'plugin_' + pluginName, null);
                }
            });

            // If the earlier cached method
            // gives a value back return the value,
            // otherwise return this to preserve chainability.
            return returns !== undefined ? returns : this;
        }
    };

}(jQuery, window, document));

根据这个就可以方便的开发jQuery插件了。
也可以使用jQuery.widget来开发一个插件,参考这里

//新插件
$.widget( "custom.superDialog", {} );
//继承原有jQuery UI插件
$.widget( "custom.superDialog", $.ui.dialog, {} );

除此之外,也可以自己写插件/模块:

(function () {
	// 将插件/模块定义在闭包私有空间里,加载时候就会立即执行,初始化
	this.PluginName = function(options) {
	}
	PluginName.prototype.doSomething = function(options) {
	}
}());

然后就可以调用了

var plugin = new PluginName(options);
plugin.doSomething();

也可以这样子:

var MODULE = (function (my) {
	my.moduleMethod = function () {
		// method override, has access to old through old_moduleMethod...
	};
	return my;
}(MODULE || {}));

#子模块,挂在MODULE这个对象/命名空间下面
MODULE.sub = (function () {
	var my = {};
	// ...

	return my;
}());

模块多了就需要模块管理工具了,比如RequireJS

//定义
define('myModule', 
    ['foo', 'bar'], 
    // module definition function
    // dependencies (foo and bar) are mapped to function parameters
    function ( foo, bar ) {
        // return a value that defines the module export
        // (i.e the functionality we want to expose for consumption)
    
        // create your module here
        var myModule = {
            doStuff:function(){
                console.log('Yay! Stuff');
            }
        }
 
        return myModule;
});
#使用
require(['app/myModule'], 
    function( myModule ){
        // start the main module which in-turn
        // loads other modules
        var module = new myModule();
        module.doStuff();
});

Javascript模块话加载可以参考这里

参考链接:
JavaScript Module Pattern: In-Depth
Advanced Plugin Concepts
Learning JavaScript Design Patterns
jQuery Plugin Pattern
理解jquery的$.extend()、$.fn和$.fn.extend()
Making Use of jQuery UI’s Widget Factory
Building Your Own JavaScript Modal Plugin
Writing Modular JavaScript With AMD, CommonJS & ES Harmony
Javascript文件加载:LABjs和RequireJS
jQuery Boilerplate——流行的jQuery插件开发模板

Jquery 滚轮事件

在项目中有采用到一个下拉列表插件jquery.ui.ufd发现在jquery 1.7以上版本中文取值过滤有问题,总是显示前几项而非过滤项。查了半天才发现是jquery的滚轮事件originalEven的问题。StatckOverflow上的解释说在1.7以上的版本jquery event对象的detail属性不再可用而应使用event.originalEvent.detail来代替。

this.listScroll.bind("DOMMouseScroll mousewheel", function(e) {
	self.stopEvent(e);
	e = e ? e : window.event;
	e=e.originalEvent;//在>1.7版本的Jquery中得这样才能获取detail/wheelDelta
	var normal = e.detail ? e.detail * -1 : e.wheelDelta / 40;
			
	var curST = self.listScroll.scrollTop();
	var newScroll = curST + ((normal > 0) ? -1 * self.itemHeight : 1 * self.itemHeight);
	self.listScroll.scrollTop(newScroll);
});

参考链接:
event.wheelDelta returns undefined
Binding mousewheel returns undefined

Jquery ContextMenu插件改造

最近需要在页面的表格上使用的右键弹出菜单,考察了下网络上的几个右键菜单发现都不太符合:需要能够用列表来布局菜单,而不是都放在javascript中控制。Jqueryui 的menu插件采用ul和li来布局,但是菜单项仅仅是个超级链接,不能结合当前被右键单击的元素进行动态传递参数和禁用/启用菜单。于是网上找了个插件自己来改造。

首先增加了几个参数:currentTarget用于保存当前的右键单击事件的对象;actions用于根据菜单id来进行函数绑定;selector用于保存当前应用右键菜单的选择器;options对象用于保存一些其他的扩展参数,比如hover为鼠标滑过菜单的css样式,highlight为当前右键单击元素的css样式。actions对象可以包括多个子对象,每一个子对象可以包含两个函数click和disabled,前者响应用户单击菜单事件,后者则指示是否在当前右键对象上禁用相关菜单。两个函数都将传入两个参数:source和li,source为右键单击的(jquery)对象,li则是当前单击的菜单(jquery)对象。 继续阅读