ddi*_*hev 22 javascript jquery design-patterns jquery-plugins
我需要构建一个jQuery插件,它将返回每个选择器id的单个实例.该插件应该并且将仅用于具有id的元素(不可能使用与许多元素匹配的选择器),因此它应该像这样使用:
$('#element-id').myPlugin(options);
Run Code Online (Sandbox Code Playgroud)
options参数应该是第一次提供,对于构造,之后我不希望构造被执行,这样我就可以像$('#element-id')一样访问插件.myPlugin()我对其他语言有很多OOP经验,但对javascript的知识有限,我真的很困惑如何做到这一点.
编辑
详细说明 - 这个插件是一个GoogleMaps v3 API包装器(帮助器)来帮助我摆脱代码重复,因为我在许多地方使用谷歌地图,通常是标记.这是当前的库(删除了大量代码,只剩下最重要的方法):
;(function($) {
/**
* csGoogleMapsHelper set function.
* @param options map settings for the google maps helper. Available options are as follows:
* - mapTypeId: constant, http://code.google.com/apis/maps/documentation/javascript/reference.html#MapTypeId
* - mapTypeControlPosition: constant, http://code.google.com/apis/maps/documentation/javascript/reference.html#ControlPosition
* - mapTypeControlStyle: constant, http://code.google.com/apis/maps/documentation/javascript/reference.html#MapTypeControlStyle
* - mapCenterLatitude: decimal, -180 to +180 latitude of the map initial center
* - mapCenterLongitude: decimal, -90 to +90 latitude of the map initial center
* - mapDefaultZoomLevel: integer, map zoom level
*
* - clusterEnabled: bool
* - clusterMaxZoom: integer, beyond this zoom level there will be no clustering
*/
$.fn.csGoogleMapsHelper = function(options) {
var id = $(this).attr('id');
var settings = $.extend(true, $.fn.csGoogleMapsHelper.defaults, options);
$.fn.csGoogleMapsHelper.settings[id] = settings;
var mapOptions = {
mapTypeId: settings.mapTypeId,
center: new google.maps.LatLng(settings.mapCenterLatitude, settings.mapCenterLongitude),
zoom: settings.mapDefaultZoomLevel,
mapTypeControlOptions: {
position: settings.mapTypeControlPosition,
style: settings.mapTypeControlStyle
}
};
$.fn.csGoogleMapsHelper.map[id] = new google.maps.Map(document.getElementById(id), mapOptions);
};
/**
*
*
* @param options settings object for the marker, available settings:
*
* - VenueID: int
* - VenueLatitude: decimal
* - VenueLongitude: decimal
* - VenueMapIconImg: optional, url to icon img
* - VenueMapIconWidth: int, icon img width in pixels
* - VenueMapIconHeight: int, icon img height in pixels
*
* - title: string, marker title
* - draggable: bool
*
*/
$.fn.csGoogleMapsHelper.createMarker = function(id, options, pushToMarkersArray) {
var settings = $.fn.csGoogleMapsHelper.settings[id];
markerOptions = {
map: $.fn.csGoogleMapsHelper.map[id],
position: options.position || new google.maps.LatLng(options.VenueLatitude, options.VenueLongitude),
title: options.title,
VenueID: options.VenueID,
draggable: options.draggable
};
if (options.VenueMapIconImg)
markerOptions.icon = new google.maps.MarkerImage(options.VenueMapIconImg, new google.maps.Size(options.VenueMapIconWidth, options.VenueMapIconHeight));
var marker = new google.maps.Marker(markerOptions);
// lets have the VenueID as marker property
if (!marker.VenueID)
marker.VenueID = null;
google.maps.event.addListener(marker, 'click', function() {
$.fn.csGoogleMapsHelper.loadMarkerInfoWindowContent(id, this);
});
if (pushToMarkersArray) {
// let's collect the markers as array in order to be loop them and set event handlers and other common stuff
$.fn.csGoogleMapsHelper.markers.push(marker);
}
return marker;
};
// this loads the marker info window content with ajax
$.fn.csGoogleMapsHelper.loadMarkerInfoWindowContent = function(id, marker) {
var settings = $.fn.csGoogleMapsHelper.settings[id];
var infoWindowContent = null;
if (!marker.infoWindow) {
$.ajax({
async: false,
type: 'GET',
url: settings.mapMarkersInfoWindowAjaxUrl,
data: { 'VenueID': marker.VenueID },
success: function(data) {
var infoWindowContent = data;
infoWindowOptions = { content: infoWindowContent };
marker.infoWindow = new google.maps.InfoWindow(infoWindowOptions);
}
});
}
// close the existing opened info window on the map (if such)
if ($.fn.csGoogleMapsHelper.infoWindow)
$.fn.csGoogleMapsHelper.infoWindow.close();
if (marker.infoWindow) {
$.fn.csGoogleMapsHelper.infoWindow = marker.infoWindow;
marker.infoWindow.open(marker.map, marker);
}
};
$.fn.csGoogleMapsHelper.finalize = function(id) {
var settings = $.fn.csGoogleMapsHelper.settings[id];
if (settings.clusterEnabled) {
var clusterOptions = {
cluster: true,
maxZoom: settings.clusterMaxZoom
};
$.fn.csGoogleMapsHelper.showClustered(id, clusterOptions);
var venue = $.fn.csGoogleMapsHelper.findMarkerByVenueId(settings.selectedVenueId);
if (venue) {
google.maps.event.trigger(venue, 'click');
}
}
$.fn.csGoogleMapsHelper.setVenueEvents(id);
};
// set the common click event to all the venues
$.fn.csGoogleMapsHelper.setVenueEvents = function(id) {
for (var i in $.fn.csGoogleMapsHelper.markers) {
google.maps.event.addListener($.fn.csGoogleMapsHelper.markers[i], 'click', function(event){
$.fn.csGoogleMapsHelper.setVenueInput(id, this);
});
}
};
// show the clustering (grouping of markers)
$.fn.csGoogleMapsHelper.showClustered = function(id, options) {
// show clustered
var clustered = new MarkerClusterer($.fn.csGoogleMapsHelper.map[id], $.fn.csGoogleMapsHelper.markers, options);
return clustered;
};
$.fn.csGoogleMapsHelper.settings = {};
$.fn.csGoogleMapsHelper.map = {};
$.fn.csGoogleMapsHelper.infoWindow = null;
$.fn.csGoogleMapsHelper.markers = [];
})(jQuery);
Run Code Online (Sandbox Code Playgroud)
它的用法看起来像这样(实际上并不完全像这样,因为有一个PHP包装器可以通过一次调用自动化它,但基本上):
$js = "$('#$id').csGoogleMapsHelper($jsOptions);\n";
if ($this->venues !== null) {
foreach ($this->venues as $row) {
$data = GoogleMapsHelper::getVenueMarkerOptionsJs($row);
$js .= "$.fn.csGoogleMapsHelper.createMarker('$id', $data, true);\n";
}
}
$js .= "$.fn.csGoogleMapsHelper.finalize('$id');\n";
echo $js;
Run Code Online (Sandbox Code Playgroud)
以上实现的问题是我不喜欢为"设置"和"地图"保留哈希映射
的$id是其中地图初始化DIV元素ID.它用作.map中的一个键,而.settings有一些映射,我在页面上为每个初始化的这样的GoogleMaps保存设置和GoogleMaps MapObject实例.在$jsOptions与$data从PHP代码是JSON对象.
现在我需要能够创建一个拥有自己的设置和GoogleMaps映射对象的GoogleMapsHelper实例,以便在我对某个元素(通过其ID)初始化之后,我可以重用该实例.但是如果我在页面上的N个元素上初始化它,它们中的每一个都应该有自己的配置,地图对象等.
我不坚持认为这是作为jQuery插件实现的!我坚持认为它具有灵活性和可扩展性,因为我将在一个大型项目中使用它,目前计划使用不同的屏幕将在几个月内使用它,因此更改它的使用界面将是重构整个项目的噩梦.
我会为此添加一笔赏金.
Dav*_*ing 19
当你说"获取"实例时,$('#element').myPlugin()我认为你的意思是:
var instance = $('#element').myPlugin();
instance.myMethod();
Run Code Online (Sandbox Code Playgroud)
这看起来似乎是一个好主意,但它被认为是扩展jQuery原型的不良做法,因为你打破了jQuery实例链.
另一种方便的方法是将实例保存在$.data对象中,因此您只需初始化插件一次,然后您可以随时使用DOM元素作为参考来获取实例,f.ex:
$('#element').myPlugin();
$('#element').data('myplugin').myMethod();
Run Code Online (Sandbox Code Playgroud)
这是我用来在JavaScript和jQuery中维护类类结构的模式(包括注释,希望你能遵循):
(function($) {
// the constructor
var MyClass = function( node, options ) {
// node is the target
this.node = node;
// options is the options passed from jQuery
this.options = $.extend({
// default options here
id: 0
}, options);
};
// A singleton for private stuff
var Private = {
increaseId: function( val ) {
// private method, no access to instance
// use a bridge or bring it as an argument
this.options.id += val;
}
};
// public methods
MyClass.prototype = {
// bring back constructor
constructor: MyClass,
// not necessary, just my preference.
// a simple bridge to the Private singleton
Private: function( /* fn, arguments */ ) {
var args = Array.prototype.slice.call( arguments ),
fn = args.shift();
if ( typeof Private[ fn ] == 'function' ) {
Private[ fn ].apply( this, args );
}
},
// public method, access to instance via this
increaseId: function( val ) {
alert( this.options.id );
// call a private method via the bridge
this.Private( 'increaseId', val );
alert( this.options.id );
// return the instance for class chaining
return this;
},
// another public method that adds a class to the node
applyIdAsClass: function() {
this.node.className = 'id' + this.options.id;
return this;
}
};
// the jQuery prototype
$.fn.myClass = function( options ) {
// loop though elements and return the jQuery instance
return this.each( function() {
// initialize and insert instance into $.data
$(this).data('myclass', new MyClass( this, options ) );
});
};
}( jQuery ));
Run Code Online (Sandbox Code Playgroud)
现在,您可以:
$('div').myClass();
Run Code Online (Sandbox Code Playgroud)
这将为找到的每个div添加一个新实例,并将其保存在$ .data中.现在,要检索某个实例的一个apply方法,你可以这样做:
$('div').eq(1).data('myclass').increaseId(3).applyIdAsClass();
Run Code Online (Sandbox Code Playgroud)
这是我多次使用的模式,非常适合我的需求.
您还可以通过添加来公开该类,以便在不使用jQuery原型的情况下使用它window.MyClass = MyClass.这允许以下语法:
var instance = new MyClass( document.getElementById('element'), {
id: 5
});
instance.increaseId(5);
alert( instance.options.id ); // yields 10
Run Code Online (Sandbox Code Playgroud)