【发布时间】:2011-10-30 22:52:48
【问题描述】:
我需要构建一个 jQuery 插件,它会为每个选择器 ID 返回一个实例。该插件应该并且只会用于具有 id 的元素(不可能使用匹配许多元素的选择器),因此应该像这样使用它:
$('#element-id').myPlugin(options);
- 我需要能够为插件提供一些私有方法以及一些公共方法。我可以做到这一点,但我的主要问题是每次调用 $('#element-id').myPlugin() 时我都想获得相同的实例。
- 而且我希望有一些代码应该只在第一次为给定 ID(构造)初始化插件时执行。
-
options参数应该第一次提供,对于构造,之后我不希望构造被执行,这样我就可以像 $('#element-id') 一样访问插件。我的插件() - 插件应该能够在同一页面上处理多个元素(通常最多 2 个)(但每个元素都需要自己的配置,再次重申 - 它们将由 ID 初始化,而不是常见的类选择器例子)。
- 上述语法只是示例 - 我愿意就如何实现该模式提出任何建议
我在其他语言方面有相当多的 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);
它的用法看起来像这样(实际上并不完全像这样,因为有一个 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;
上述实现的问题是我不喜欢为“设置”和“地图”保留哈希映射
$id 是初始化地图的 DIV 元素 ID。它用作 .map 和 .settings 中的键,其中我保存页面上每个已初始化的此类 GoogleMaps 的设置和 GoogleMaps MapObject 实例。 PHP 代码中的$jsOptions 和$data 是JSON 对象。
现在我需要能够创建一个 GoogleMapsHelper 实例来保存它自己的设置和 GoogleMaps 地图对象,以便在我对某个元素(通过其 ID)初始化它之后,我可以重用该实例。但是如果我在页面上的 N 个元素上初始化它,每个元素都应该有自己的配置、地图对象等。
我不坚持这是作为一个 jQuery 插件实现的!我坚持认为它是灵活且可扩展的,因为我将在一个大型项目中使用它,其中包含十多个当前计划的不同屏幕几个月后就会被使用,改变它的使用界面将是对整个项目进行重构的一场噩梦。
我会为此添加赏金。
【问题讨论】:
-
坦率地说,在不了解这个插件的情况下,作为一个有经验的 jQuery 用户,这整个“不可能使用匹配许多元素的选择器”的事情将是一个严重的问题。如果你正在制作一个 jQuery 插件,它应该像其他 jQuery 插件一样工作,除非它真的在做一些真正独特的事情。
-
您的问题中有很多 wants,但只有一行代码(从调用者的角度来看,这无济于事)。你能详细说明你尝试了什么吗?如果您发布更多代码,您的问题可能会有答案。也就是说,如果我理解正确,您需要一个每元素单例,它是通过只接受这个单个元素的方法创建的。这与我所知道的 jQuery 和插件引擎的根本概念相反。
-
很抱歉给您带来了困惑。它实际上是 GoogleMaps JS API 的助手和包装器。它确实是独一无二的,因为它旨在通过我在许多地方使用带有标记的谷歌地图的项目来节省我的代码重复。它不打算在项目之外使用。由于我太累了,我将发布一些我为此目的构建的当前实现的助手的代码,我觉得它的代码不是正确的方法。
-
我用更多细节编辑了我的问题,但如果有任何不清楚的地方,请告诉我如何改进我的问题。
-
谷歌地图 v3 不需要 需要 ID,DOM 元素就可以了,因此您可以在 jQuery 中使用任何选择器。
标签: javascript jquery design-patterns jquery-plugins