I was introduced two years ago to the jQuery javascript library via the thickbox script which, among other things, allows to load external urls inside a modal window, a neat feature enhancing user experience as it provides a more visual continuity between the current window and the next.

jQuery is a surprisingly easy library to get your hands on, even for designers and server-side developers such as me, and as I used it more and more in my every day development, quickly grew in me emotions and feelings of true love towards this splendid library and its outstanding user community. Pop into the mailinglist, you will be amazed by the number of souls dedicated to helping each other. Some politician leaders should learn from it (is there a jquery plugin for solving the everlasting Belgian political crisis ? could be fun :) )

Anyway, among the zillion jquery plugins, one such plugin offers a much more powerful and flexible way to launch modal windows: jqModal.

At first jqModal may not seem as easy to use for novices (unlike thickbox which basically only requires you to add a class to all links that should load into the modal window, thus staying clear of javascript, how convenient). The truth is, once you get your first attempts working, jqModal is just as easy, it just needs the designer to mentally “accept” dealing with a little bit of javascript.

Yet, although quite well documented, the jqModal plugin website does not describe the precise use-case of loading into an iframe, so here is a rundown on how I do it.

The final result of thickbox > jqModal example is in action here

1./ html header:

Include the jqModal script and stylesheet in your header. Make sure you have jquery too !

<link rel="stylesheet" type="text/css" media="screen" href="jqModal.css"/>
<script src="jquery-1.2.6.pack.js" type="text/javascript"></script>
<script src="jqModal.js" type="text/javascript"></script>

2./ the html describing the modal window structure

jqModal let you define your own modal window html, and uses special classes, such as .jqmClose, which defines a button with a close functionality. (Note that you don’t have to provide the html for the modal layer itself. ) Since we want to load an external url, we will use an IFRAME as container. As a habit, I usually put modal window html code at the very end of the body element.

<div id="modalWindow" class="jqmWindow">
        <div id="jqmTitle">
            <button class="jqmClose">
                Close X
            </button>
            <span id="jqmTitleText">Title of modal window</span>
        </div>
        <iframe id="jqmContent" src="">
        </iframe>
    </div>

3./ the css

Include the jqModal css properties and customize to your liking So, in addition to the jqModal stylesheet, i added this styling:


.jqmClose{ background:#FFDD00; border:1px solid #FFDD00; color:#000; clear:right; float:right; padding:0 5px; cursor:pointer; }
.jqmClose:hover{ background:#FFF; }
#jqmContent{ width:99%; height:99%; display: block; clear:both; margin:auto; margin-top:10px; background:#111; border:1px dotted #444; }

4./ the javascript
Add the following to your $(document).ready event handling function:


var loadInIframeModal = function(hash){
    var $trigger = $(hash.t);
    var $modal = $(hash.w);
    var myUrl = $trigger.attr('href');
    var myTitle= $trigger.attr('title');
    var $modalContent = $("iframe", $modal);

    $modalContent.html('').attr('src', myUrl);
    //let's use the anchor "title" attribute as modal window title
    $('#jqmTitleText').text(myTitle);
    $modal.jqmShow();
}
// initialise jqModal
$('#modalWindow').jqm({
modal: true,
trigger: 'a.thickbox',
target: '#jqmContent',
onShow:  loadInIframeModal
});

});

Basically, the loadInIframeModal function controls what should happen when a jqModal is to be shown: here, it assigns the clicked link’s URI to the IFRAME SRC attribute, thus loading the link inside the jqModal iframe, updates the modal title and then reveals the modal.

Then, the jqm() call initializes the jqModal DOM object, specifying that it should be a modal behaviour (nothing clickable outside the window itself), it will be launched by anchor links with class “thickbox”, and the result will be loaded in the iframe with id “jqmContent”.

It wasn’t too difficult wasn’t it ? I told you jqModal is flexible. In fact, i think it’s the swiss-army knife of all modal windowing !

Converting your website from thickbox to jqModal

I used thickbox on a very large Content Management website, but wanted to get the snappier feel of jqModal.
Not too difficult, except that thickbox parameters are included in the href attribute itself, as additional query vars.

Here is an example of such url:

writing.php?todo=display&writing_id=2&KeepThis=true&TB_iframe=true&width=90%&height=99%

In this example, the thickbox parameters are KeepThis,TB_iframe,width and height, which i had to translate into something jqModal could manage. The parameters i was particularly interested in are the “width” and “height”, allowing to modify the jqModal window dimensions.

Therefore I created a wrapper function that would look into the querystring for the width and height parameters, and modify the window accordingly.
Note that i wanted the modal window to always stay in the middle of the screen, so redimensioning it means also modifying its position on the screen.
this explains the long code. And also the will to accomodate for percentage resizing (such as in the example above : “width=90%”).
Now, it’s quite bloated but it works, ok ? :-)

See the thickbox > jqModal example in action here

All suggestions for improvement are welcomed though.

12 OCTOBER 2008 UPDATE: Added a new query vars, that specify if the parent window should be reloaded or not: jqmRefresh (default to true in this case)

   $(document).ready(function(){
        //thickbox replacement
    var closeModal = function(hash)
    {
        var $modalWindow = $(hash.w);

        //$('#jqmContent').attr('src', 'blank.html');
        $modalWindow.fadeOut('2000', function()
        {
            hash.o.remove();
            //refresh parent
            if (hash.refreshAfterClose == true)
            {
                window.location.href = document.location.href;
            }
        });
    };
    var openInFrame = function(hash)
    {
        var $trigger = $(hash.t);
        var $modalWindow = $(hash.w);
        var $modalContainer = $('iframe', $modalWindow);

        var myUrl = $trigger.attr('href');

        var myTitle = $trigger.attr('title');
        var newWidth = 0, newHeight = 0, newLeft = 0, newTop = 0;
        $modalContainer.html('').attr('src', myUrl);
        $('#jqmTitleText').text(myTitle);
        myUrl = (myUrl.lastIndexOf("#") > -1) ? myUrl.slice(0, myUrl.lastIndexOf("#")) : myUrl;
        var queryString = (myUrl.indexOf("?") > -1) ? myUrl.substr(myUrl.indexOf("?") + 1) : null;

        if (queryString != null && typeof queryString != 'undefined')
        {
            var queryVarsArray = queryString.split("&");
            for (var i = 0; i < queryVarsArray.length; i++)
            {
                if (unescape(queryVarsArray[i].split("=")[0]) == 'width')
                {
                    var newWidth = queryVarsArray[i].split("=")[1];
                }
                if (escape(unescape(queryVarsArray[i].split("=")[0])) == 'height')
                {
                    var newHeight = queryVarsArray[i].split("=")[1];
                }
                if (escape(unescape(queryVarsArray[i].split("=")[0])) == 'jqmRefresh')
                {
                    hash.refreshAfterClose = queryVarsArray[i].split("=")[1]
                } else
                {
                    hash.refreshAfterClose = true;
                }
            }
            // let's run through all possible values: 90%, nothing or a value in pixel
            if (newHeight != 0)
            {
                if (newHeight.indexOf('%') > -1)
                {

                    newHeight = Math.floor(parseInt($(window).height()) * (parseInt(newHeight) / 100));

                }
                var newTop = Math.floor(parseInt($(window).height() - newHeight) / 2);
            }
            else
            {
                newHeight = $modalWindow.height();
            }
            if (newWidth != 0)
            {
                if (newWidth.indexOf('%') > -1)
                {
                    newWidth = Math.floor(parseInt($(window).width() / 100) * parseInt(newWidth));
                }
                var newLeft = Math.floor(parseInt($(window).width() / 2) - parseInt(newWidth) / 2);

            }
            else
            {
                newWidth = $modalWindow.width();
            }

            // do the animation so that the windows stays on center of screen despite resizing
            $modalWindow.css({
                width: newWidth,
                height: newHeight,
                opacity: 0
            }).jqmShow().animate({
                width: newWidth,
                height: newHeight,
                top: newTop,
                left: newLeft,
                marginLeft: 0,
                opacity: 1
            }, 'slow');
        }
        else
        {
            // don't do animations
            $modalWindow.jqmShow();
        }

    }

    $('#modalWindow').jqm({
        overlay: 70,
        modal: true,
        trigger: 'a.thickbox',
        target: '#jqmContent',
        onHide: closeModal,
        onShow: openInFrame
    });
});

I hope that you enjoyed the read, let me know if you have any questions, ok ?