Jquery plugin scope/best practice

Hi,

I’ve search for this but couldn’t find a suitable answer.

Basically I’m making a new plugin, and I’m using the format from this site:

However, I am having trouble with variable scope (or maybe I’m doing it the wrong way).

If I have this (truncated so it’s easier to read):

(function($) 
{
	$.rp_popup = { 'version' : "0.0.1" };
	$.fn.rp_popup = function(options)
	{
		var $element = $(this);
		var options = $.extend({}, $.rp_popup.defaults, options);
		
		$element.click(function()
		{
			return false;
		});
	};
	$.rp_popup.open_popup = function()
	{
                // I want to access options here
		debug(options);
	}
	$.rp_popup.defaults = {
		modal		: false	
	}
})(jQuery);

Ignore the fact the plugin does nothing :blush:

How can I access options from the open_popup function? I know I can put it inside the rp_popup function but that seems a little hacky (although this is the way pretty photo does it).

And jquery UI does it in a way where you pass a function as a string, and then uses eval to call it, so:

$.rp_popup("open")

but I’m not sure about that.

So yea, sorry for the long post, but what’s the best way to do it?

Cheers in advance :slight_smile:

Just tried that actually and I can’t access the variables when I place them outside of the functions

Please show your code that is having the issue and we’ll get you back on track.

It helps to know that using var to declare a variable inside a function does clobber the variable of the same name from a higher scope.

In other words, to access a variable from a higher scope (such as outside of a function) do not use the var keyword inside the function for that variable.

Below is what I have. It’s all working, just not sure if it’s best practice.

I have to pass the options var in to the open function because otherwise if applied this to two elements, it’s only keep the latest’s value.

Sorry for the long post!

/*
	TODO	Allow custom classes for errors/rows
*/
(function($) {
	
	$.rpPopup = { 'version' : "0.0.1" };
		
	$.fn.rpPopup = function(options)
	{
		var options = $.extend({}, $.rpPopup.defaults, options);
		var $element = $(this);
		var $backgroundDiv;
		var $closeButton;
		var $popupCont;
		var	popupActive = false;
		
		// Click to open
		$element.click(function()
		{	
			$.rpPopup.open(options);
			return false;
		});
	
		// Opening the popup
		$.rpPopup.open = function(options)
		{
			// Create Items
			$backgroundDiv = $('<div id="rp_popup_background" />')
				.appendTo($(document.body))
				.css({
					 "opacity" : options.backgroundOpacity
				});
			$closeButton = $('<a id="rp_close_button" href="#">Close</a>')
				.appendTo($(document.body));
			$popupCont = $('<div id="' + options.popupId + '" />')
				.appendTo($(document.body));
			
			// Close button click
			$closeButton.click(function()
			{
				$.rpPopup.close();
				return false;
			});
		
			// Background click to close
			$backgroundDiv.click(function()
			{
				if (!options.modal)
					$.rpPopup.close();
				return false;
			});
			
			var $loaderGif = $('<img src="' + options.loaderGif + '" />');
			
			popupActive = true;
			$backgroundDiv.fadeIn("fast");
			$popupCont.html($loaderGif);
			$.rpPopup.centre();
			$popupCont.css({
				"display"	: "block",
				"opacity"	: 0
			}).animate({
				opacity		: 1
			}, "fast");
			
			if (options.content)
			{
				$popupCont.load(options.content, function(data)
				{
					$closeButton.fadeIn("fast");
					var $content = $(data);
					if ($content.find("form").length > 0)
					{
						var $form = $("#" + $content.find("form").attr("id"));
						$form.submit(function(event){
							$content.prepend($loaderGif);
							$.rpPopup.centre();
							if (options.validationUrl)
							{
								$.post(options.validationUrl, $form.serialize(), function(response){
																						  
									// Remove any errors
									$('.form_row').removeClass('invalid_row');
									$form.find('.error').remove();
									$loaderGif.remove();
									
									if(response.status == "error") 
									{
										$.each(response.errors, function(key){
											$('#' + key).parents('.form_row').addClass('invalid_row');
										});
				
										$form.prepend(response.feedback);
									}
									else
									{
										if (options.validationCallbackUrl)
											window.location = options.validationCallbackUrl;
										else if (response.feedback)
											$form.before(response.feedback).remove();
										else
											$.rpPopup.close();
									}
									$.rpPopup.centre();
									
								}, "json");
							}
							else
							{
								debug("No validationUrl supplied");
							};
							return false;
						});
					}
					$.rpPopup.centre();
				});
			}
			else
			{
				$popupCont.html("<p style=\\"background:#fff;padding:10px;width:150px;text-align:centre\\">No content can be loaded</p>");
				$.rpPopup.centre();
				debug("No content to load");
			}
			
		}
		
		// Closing the popup
		$.rpPopup.close = function()
		{
			if (popupActive)
			{
				popupActive = false;
				$backgroundDiv.fadeOut("fast");
				$popupCont.fadeOut("fast");
				$closeButton.fadeOut("fast", function() {
					$backgroundDiv.remove();
					$popupCont.remove();
					$closeButton.remove();
				});
			}
		}
		
		// Centering the popup
		$.rpPopup.centre = function()
		{
			var popupWidth = options.popupWidth ? parseInt(options.popupWidth) : $popupCont.children().outerWidth();
			var popupHeight = options.popupHeight ? parseInt(options.popupHeight) : $popupCont.children().outerHeight();
			$popupCont.css({
				"width"		: popupWidth,
				"height" 	: popupHeight
			});
			var windowWidth = document.documentElement.clientWidth;
			var windowHeight = document.documentElement.clientHeight;
			$popupCont.css({
				"bottom"	: windowHeight / 2  - popupHeight / 2,
				"left"		: windowWidth / 2  - popupWidth / 2
			});
			// only need force for IE6
			$backgroundDiv.css({
				"height": windowHeight
			});
			// Position close button
			$closeButton.css({
				"bottom"	: windowHeight / 2  + popupHeight / 2,
				"left"		: windowWidth / 2  + popupWidth / 2 - $closeButton.width() - 20
			});
		}
		
		$(window).resize(function() 
		{
			if (popupActive)
			{
				$.rpPopup.centre();
			}
		});	
	
	};
		
	// Default options
	$.rpPopup.defaults = {
		modal					: false,
		popupWidth				: null,
		popupHeight				: null,
		backgroundOpacity		: 0.7,
		popupId					: "rp_popup_cont",
		loaderGif				: "/graphics/rp-popup/ajax_loader.gif",
		content					: "",
		validationUrl			: "",
		validationCallbackUrl	: ""
	};
	
	// Debug - so IE doesn't have a fit
	function debug(value)
	{
		if (window.console && window.console.log)
		{
			console.log(value);
		}
	}

})(jQuery);

Cheers pmw57. Would those variables only be available within the function enclosure then?

I think i’m getting confused because I’m seeing this as more of a class than a function, which it isn’t.

What you could do as an alternative, is to move the var statement for that variable up out of the function, so that it is then accessible from both functions.

I would kindly suggest you don’t advise someone on something that you yourself are unsure about, like in this case. Not only was your advice ambiguous, it also makes no sense in the context of answering the original question.

Hi,
I think also a each is missing


 return this.each(function() {     
   });  

I found this http://blog.jeremymartin.name/2008/02/building-your-first-jquery-plugin-that.html very useful
Bye

@Blue - It’s the other way around isn’t it? “var” makes them local? I guess I’ll have to pass them in then as parameters, which does make sense. Cheers.

@wisher - Cheers for the link, looks good. In my case, wrapping it in an each loop wouldn’t be necessary I don’t think.

By using “var” keyword, you are making a variable visible globally.
You can also pass that $.rp_popup to your function as a parameter.