Tom Lauck’s Deseloper.org

A Simple Modal – Redux

author: tom

Over a year ago I walked through an example of creating the basic foundation for a modal window using jQuery.  The goal was simplicity, minimalism, and flexibility to add-on functionality as needed.

As stated at the outset, this post was meant to illustrate a bare bones and simple example of a modal window. If you wanted to extend the functionality for example, it would be quite simple to [...] suit needs.

Last Year’s A Simple Modal Post

The example was well received in feedback from readers, and there was some constructive criticism as well. Equal to the amount of people that appreciated the minimalistic approach were requests from readers to support IE6, forms, and a host of other odds and ends.  Some readers had some useful feedback for features and additions that I purposefully did not include in an effort to keep things basic.

Therefore, I thought there was a nice opportunity to revisit the basic modal window script and make it a bit more comprehensive in an effort to suit the needs and requests of the aforementioned readers.  So before we get into the code, lets outline some things that have changed:

  • Converted for use as a jQuery plugin.
  • Added IE6 compatibility
  • Added Opera compatibility
  • Added ability to fade in and out
  • Added multiple ways of setting parameters
  • Added multiple content types (’image’ and ‘iframe’)

View Example | Download Source

The Plugin

(function ($) {

	/**********************************
	* CUSTOMIZE THE DEFAULT SETTINGS
	* Ex:
	* var _settings = {
	* 	id: 'modal',
	* 	src: function(sender){
	*		return jQuery(sender).attr('href');
	*	},
	* 	width: 800,
	* 	height: 600
	* }
	**********************************/
	var _settings = {
		width: 800, // Use this value if not set in CSS or HTML
		height: 600, // Use this value if not set in CSS or HTML
		overlayOpacity: .85, // Use this value if not set in CSS or HTML
		id: 'modal',
		src: function (sender) {
			return jQuery(sender).attr('href');
		},
		fadeInSpeed: 0,
		fadeOutSpeed: 0
	}

	/**********************************
	* DO NOT CUSTOMIZE BELOW THIS LINE
	**********************************/
	$.modal = function (options) {
		return _modal(this, options);
	}
	$.modal.open = function () {
		_modal.open();
	}
	$.modal.close = function () {
		_modal.close();
	}
	$.fn.modal = function (options) {
		return _modal(this, options);
	}
	_modal = function (sender, params) {
		this.options = {
			parent: null,
			overlayOpacity: null,
			id: null,
			content: null,
			width: null,
			height: null,
			modalClassName: null,
			imageClassName: null,
			closeClassName: null,
			overlayClassName: null,
			src: null
		}
		this.options = $.extend({}, options, _defaults);
		this.options = $.extend({}, options, _settings);
		this.options = $.extend({}, options, params);
		this.close = function () {
			jQuery('.' + options.modalClassName + ', .' + options.overlayClassName).fadeOut(_settings.fadeOutSpeed, function () { jQuery(this).unbind().remove(); });
		}
		this.open = function () {
			if (typeof options.src == 'function') {
				options.src = options.src(sender)
			} else {
				options.src = options.src || _defaults.src(sender);
			}

			var fileExt = /^.+\.((jpg)|(gif)|(jpeg)|(png)|(jpg))$/i;
			var contentHTML = '';
			if (fileExt.test(options.src)) {
				contentHTML = '<div class="' + options.imageClassName + '"><img src="' + options.src + '"/></div>';

			} else {
				contentHTML = '<iframe width="' + options.width + '" height="' + options.height + '" frameborder="0" scrolling="no" allowtransparency="true" src="' + options.src + '"></iframe>';
			}
			options.content = options.content || contentHTML;

			if (jQuery('.' + options.modalClassName).length && jQuery('.' + options.overlayClassName).length) {
				jQuery('.' + options.modalClassName).html(options.content);
			} else {
				$overlay = jQuery((_isIE6()) ? '<iframe src="BLOCKED SCRIPT\'<html></html>\';" scrolling="no" frameborder="0" class="' + options.overlayClassName + '"></iframe><div class="' + options.overlayClassName + '"></div>' : '<div class="' + options.overlayClassName + '"></div>');
				$overlay.hide().appendTo(options.parent);

				$modal = jQuery('<div id="' + options.id + '" class="' + options.modalClassName + '" style="width:' + options.width + 'px; height:' + options.height + 'px; margin-top:-' + (options.height / 2) + 'px; margin-left:-' + (options.width / 2) + 'px;">' + options.content + '</div>');
				$modal.hide().appendTo(options.parent);

				$close = jQuery('<a class="' + options.closeClassName + '"></a>');
				$close.appendTo($modal);

				var overlayOpacity = _getOpacity($overlay.not('iframe')) || options.overlayOpacity;
				$overlay.fadeTo(0, 0).show().not('iframe').fadeTo(_settings.fadeInSpeed, overlayOpacity);
				$modal.fadeIn(_settings.fadeInSpeed);

				$close.click(function () { jQuery.modal().close(); });
				$overlay.click(function () { jQuery.modal().close(); });
			}
		}
		return this;
	}
	_isIE6 = function () {
		if (document.all && document.getElementById) {
			if (document.compatMode && !window.XMLHttpRequest) {
				return true;
			}
		}
		return false;
	}
	_getOpacity = function (sender) {
		$sender = jQuery(sender);
		opacity = $sender.css('opacity');
		filter = $sender.css('filter');

		if (filter.indexOf("opacity=") >= 0) {
			return parseFloat(filter.match(/opacity=([^)]*)/)[1]) / 100;
		}
		else if (opacity != '') {
			return opacity;
		}
		return '';
	}
	_defaults = {
		parent: 'body',
		overlayOpacity: 85,
		id: 'modal',
		content: null,
		width: 800,
		height: 600,
		modalClassName: 'modal-window',
		imageClassName: 'modal-image',
		closeClassName: 'close-window',
		overlayClassName: 'modal-overlay',
		src: function (sender) {
			return jQuery(sender).attr('href');
		}
	}
})(jQuery);

As you can see, quite a bit has changed – which is not necessarily a bad thing.  There is now tighter integration with jQuery and more out of the box configuration ability as a result.

Adding Some Style

.modal-overlay {
	position: fixed;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	height: 100%;
	width: 100%;
	margin: 0;
	padding: 0;
	background: #131313;
	opacity: .85;
	filter: alpha(opacity=85);
	z-index: 101;
}
.modal-window {
	position: fixed;
	top: 50%;
	left: 50%;
	margin: 0;
	padding: 0;
	z-index: 102;
	background: #fff;
	border: solid 8px #000;
	-moz-border-radius: 8px;
	-webkit-border-radius: 8px;
}
.close-window {
	position: absolute;
	width: 47px;
	height: 47px;
	right: -23px;
	top: -23px;
	background: transparent url(../images/close-button.png) no-repeat scroll right top;
	text-indent: -99999px;
	overflow: hidden;
	cursor: pointer;
}

Implementation

<a href="modal.html" onclick="$(this).modal({width:833, height:453}).open(); return false;">Modal iFrame</a>
<a href="images/your-image.jpg" onclick="$(this).modal({width:500, height:375}).open(); return false;">Modal Image</a>

Although a little more weight has been added to the script itself, the implementation is more flexible and can be used with just about any jQuery chain – whether its onclick, a document being loaded, you name it.  There is also an added ability to set parameters in your area of choosing – inside the CSS, in the plugin itself, or in the method call or jQuery chain object.

Moving Forward

So if you are looking for something extra simple, head on over to last years post and in the meantime enjoy the added functionality with this updated example.

View Example | Download Source

  • Share/Bookmark

15 Responses

date: October 22nd, 2009

You should expand this to have a bounding context, so that it can be centered on a specific element on your screen. Say a dynamically loaded AJAX panel.

To keep the current functionality defaulted to the whole screen just set the bounding context to the document.

Do you think this would be possible?

spoken by: Nick Berardi

date: October 22nd, 2009

It’s not incredibly obvious, but the parent property can be overridden and used to position over an element – either in the call or in the settings for the plug-in. The caveat is that CSS would need to be used to set the bounding instead of JavaScript – which isn’t a bad thing if you think of web pages in MVC terms.

I’ll look into expanding on this and some clarification around it.

spoken by: tom

date: October 22nd, 2009

Nice work!

spoken by: Jody

date: October 22nd, 2009

Tom,

The author of SimpleModal here, congrats on the updated version. I just have a couple of thoughts to share with you:

1) I’ve been using the $().modal and $.modal() namespace in jQuery for a couple of years now. If you are planning on adding this to the jQuery plugins site, you might consider changing yours to something that won’t conflict.

2) Because you are aliasing the jQuery object with $, you can switch all of your jQuery() calls to $(). Not a big deal, but will save on some byte size ;)

3) Your download source link is getting a 500 server error.

spoken by: Eric Martin

date: October 23rd, 2009

1. There are no real plans on adding this to the jQuery plugin site – again, this was originally meant as more of an example to build off of.

2. Normally that would be correct. However, in this case $(this) is allowing the plugin to get the target source based on the href attribute of the element the onclick it is on. If we were to call the modal window object (for instance, as there is on the modal.html where I use the aliasing you describe), that may be the preferred method.

3. There was an issue with Google App Engine which is resolved now.

spoken by: tom

date: December 11th, 2009

The zip file is corrupted.

spoken by: Alex

date: December 11th, 2009

Also the ‘copy to clipboard’ doesnt seem to work on Windows XP+Firefox 3.5.5

spoken by: Alex

date: December 17th, 2009

@alex I double checked the zip file and everything checked out fine. If you have more details about the process you took to download them, please send them to me – I couldn’t duplicate it.

Also, the code highlighting plugin I am using is a bit dated. I’m currently redesigning the site, so keep an eye out for an update – with better syntax highlighting ;)

spoken by: tom

date: December 17th, 2009

I wanted a modal I could dynamically pass the width and height parameters to from my href onclick event, yours works great because of this functionality. Thank you!

spoken by: steve

date: December 23rd, 2009

is it possible to make the modal truly modal in that you cannot close it/ignore it by clicking on the rest of the page etc?
currently the modal allows you to click elsewhere and close the modal.

spoken by: csaket

date: January 13th, 2010

very cool script. One issue that I have found is that if I launch the window, then close it and then try and call a seperate jscript function that has window.open in it the jscript fails. It is conflicting with the modal.open. If I rename the modal .open’s to openM for example then everything is ok. I can’t figure out how to sort it though.

spoken by: Tim

date: February 9th, 2010

How can i submit AND close a form in the modal window? .. the example only close and not submit (via post or get). Can you help me with that?

spoken by: villas

date: February 9th, 2010

Submit and Close

Just close and NOT submit the field … how can i submit the form?

spoken by: villas

date: February 14th, 2010

Hi, On closing the modal, I want to refresh the parent window. Can you please give me example. Thanks.

spoken by: Ayya

date: March 6th, 2010

I did not understood exactly, how can i make this happen on page load automatically and not on link click, if anyone can tell me this,

Thank You All!

spoken by: carlos

Leave a Reply

Oct 21 2009