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

22 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

date: April 18th, 2010

//place this code in the modal window after all action had been done and you want to close the modal window and refresh the source page.
//the reloadsorce reloads the source screen where the link to the modal window is.
//the doit function first closes the modal window and then calls the reloadSource function. Grtz mediazone.nl :-)
function reloadSource() {
var p_ref=parent.location.href;
parent.location.href=(p_ref);
}

function doit() {
parent.$.modal().close()
reloadSource();
}
doit();

spoken by: Ron

date: April 28th, 2010

Hi firstly thank you for the script. I have a question. I want to use auto height. but I can not use. I wonder how I can do.

spoken by: Coco

date: April 30th, 2010

how do i click a link inside the modal-window and got a new modal-window without the parent window

spoken by: alon

date: June 1st, 2010

I’m really new to this and your tutorial was great. I was easily able to implement this and modify it. However, I cannot for the life of me get the close icon to show up. The first article on this page will show you what I mean:

http://papergodmother.com/test/wedding_invitation_tips_and_advice.html#

Anyone’s help (or even a point to the correct forum for the question) would be greatly appreciated!

spoken by: tiffani

date: June 1st, 2010

One more thing. I’m trying to make the height adjustable to the content and I can’t seem to do that. Any advice?

spoken by: tiffani

date: June 1st, 2010

tiffani,

You’re eliminating the background image for .close-window near the end of your CSS file. Remove the “background: url();” from the last .close-window selector (I guess leave the “filter” line) and see if that helps.

E.

spoken by: erat

date: July 13th, 2010

Hi, Thanks for provide the code. I was planning to post changes on my blog but actually I don’t like post code in blogs (I like to read only). So.. I’ve some improvements on your code so that it would be really reusable. You shouldn’t be dependent on html anchor object at all. Anyway.. I still thank you. Have my improvements.

Eduardo Xavier
BendingBits.com

///

/************************************************************************************************************
* SIMPLE MODAL v 2.0
* © 2009 FISHBOWL MEDIA LLC
* http://fishbowlmedia.com
***********************************************************************************************************/
(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
url:”,
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 () {

var fileExt = /^.+\.((jpg)|(gif)|(jpeg)|(png)|(jpg))$/i;
var contentHTML = ”;

if (options.url!=”)
{
contentHTML = ”;
}
else
{
if (typeof options.src == ‘function’) {
options.src = options.src(sender)
} else {
options.src = options.src || _defaults.src(sender);
}

if (fileExt.test(options.src)) {
contentHTML = ”;

} else {
contentHTML = ”;
}
}

options.content = options.content || contentHTML;

if (jQuery(‘.’ + options.modalClassName).length && jQuery(‘.’ + options.overlayClassName).length) {
jQuery(‘.’ + options.modalClassName).html(options.content);
} else {
$overlay = jQuery((_isIE6()) ? ” : ”);
$overlay.hide().appendTo(options.parent);

$modal = jQuery(” + options.content + ”);
$modal.hide().appendTo(options.parent);

$close = jQuery(‘‘);
$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);

spoken by: Eduardo Xavier

Leave a Reply

Oct 21 2009