Window (Dialog)

As part of a new UI library, I have put together a Window (Dialog) feature using the jQuery UI Dialog in a manner that follows the guidelines found in the Webgrown Solutions UI Manifesto. The Window (Dialog) supports the following features:

  • Automatically open window on page load.
  • Automatically open window on timeout event.
  • Automatically close window on timeout event.
  • Open from user click event (link, button, etc.)
  • Set window title.
  • Load window body content from various sources (inline HTML, page element, URL - iframe, or Ajax).
  • Fully control the presentation of the window (position, size, etc.)
  • Fully control the user's ability to interact with the window (resize the window, close the window, move the window).
  • Make the window modal or not.
  • UI Manifesto compliance:

The Demo

Example #1: On Page Load Event (Content Source: Inline HTML)

A Window will automatically open when the page loads, and that Window's content will simply be the inline HTML within the requesting element. After five seconds the Window will automatically close.

The code to automatically open this example's Window does not have
a visible presence on the page.
This is a div tag, which which will never show in the page and will always open onload.

Example #2: Link (Content Source: Page Element)

Clicking the link will open a Window, and that Window's content will come from a hidden element on the page.

Open Window (Content Source: Page Element)!

Sample image.

Vivamus dignissim consectetur magna eu consequat. Etiam mattis libero ac urna lacinia quis tincidunt tellus accumsan. Nullam semper urna sit amet risus auctor placerat. Nulla facilisi. Maecenas ut cursus purus. Vivamus mollis libero ac turpis venenatis id cursus nunc hendrerit. Sed nec nulla augue. Mauris orci felis, egestas commodo pulvinar id, mollis vitae est.

Ut blandit venenatis arcu id euismod. Nunc faucibus, diam et euismod pretium, eros nulla pulvinar nibh, vitae ornare urna arcu eu orci. Vivamus sit amet lectus a dolor laoreet viverra. Donec et justo neque. Pellentesque eleifend justo in ante bibendum tempus. Sed pellentesque euismod faucibus.

Example #3: Link (Content Source: URL - iframe)

Clicking the link will open a Window, and that Window's content will be loaded into an iframe from some internal or external URL.

Open Window (Content Source: External URL)!

Example #4: Button (Content Source: Ajax + Ajax Form)

Clicking the button will open a Window, and that Window's content will be loaded using an Ajax request. The content loaded will be an Ajax Form, which will allow an Ajax Post to be made to a service, a success action to be performed, and then the Window will be automatically closed.

Example #5: Timing Event (Content Source: Inline HTML)

Two Windows will automatically be opened when a timeout event expires, and the Windows' content will simply be the inline HTML within the requesting element. The first Window will open three minutes after the page loads. The first Window will then automatically close after 30 seconds. Immediately following the close of the first Window, a second Window will automatically open.

The code to automatically open this example's two Windows does not have
a visible presence on the page.
Your session will expire in 30 seconds. Please finish what you are doing before your session times out.
Your session has expired! I hope you did not loose any information you were working on. I tried to warn you. :-)

The Markup

The Window (Dialog) is implemented just using some basic page element (<div>, <a>, <input type="button" ...>, etc.), with a normal class attribute and some custom data (data-*) attributes to allow feature customization. Once the CSS and Script stuff are in place on a website, any page can implement the Window (Dialog) by simply setting the applicable page element with the class attribute to "window", and setting the desired optional custom data attributes. The custom data attributes are optional, so the window could simple have only the standard markup plus the class attribute (depending on the page element used).

That sounds like a DRY solution to me (see UI Manifesto).

 
<h3>Example #1: On Page Load Event (Content Source: Inline HTML)</h3>
<p>
    A Window will automatically open when the page loads, and that Window's content will simply be the inline HTML within the requesting element. 
    After five seconds the Window will automatically close.
</p>
<pre>
    The code to automatically open this example's Window does not have<br />a visible presence on the page.
</pre>
<div id="hdivWindowTest1" class="window" 
    data-openOnLoad="true"
    data-openOnTimeoutInMilliseconds=""
    data-closeOnTimeoutInMilliseconds="5000"
    data-windowTitle="Example #1: On Page Load Event (Content Source: Inline HTML)"
    data-contentSourceType="InlineHTML"
    data-contentSourceValue=""
    data-additionalWindowClass=""
    data-height="200"a
    data-width="300"
    data-maxHeight="400"
    data-maxWidth="500"
    data-minHeight="100"
    data-minWidth="200"
    data-allowCloseByEscKey="true"
    data-hideCloseByX="false"
    data-isDraggable="true"
    data-isModal="false"
    data-isResizable="true"
    data-position="center">
    This is a div tag, which which will never show in the page and will always open onload.
</div>
        
<h3>Example #2: Link (Content Source: Page Element)</h3>
<p>
    Clicking the link will open a Window, and that Window's content will come from a hidden element on the page.
</p>
<p>
    <a id="haWindowTest2" class="window" 
        data-openOnLoad="false"
        data-openOnTimeoutInMilliseconds=""
        data-closeOnTimeoutInMilliseconds=""
        data-windowTitle="Example #2: Link (Content Source: Page Element)"
        data-contentSourceType="Element"
        data-contentSourceValue="hdivWindowTest2Content"
        data-additionalWindowClass=""
        data-height="300"
        data-width="500"
        data-maxHeight=""
        data-maxWidth=""
        data-minHeight=""
        data-minWidth=""
        data-allowCloseByEscKey="false"
        data-hideCloseByX="false"
        data-isDraggable="false"
        data-isModal="true"
        data-isResizable="false"
        data-position="['center','top']">Open Window (Content Source: Page Element)!</a>
</p>
<div id="hdivWindowTest2Content" class="hide">
    <img src="/images/html5-badge-v-css3.png" alt="Sample image." style="float:right; margin:0px 0px 6px 6px;" />
    <p>Vivamus dignissim consectetur magna eu consequat. Etiam mattis libero ac urna lacinia quis tincidunt tellus accumsan. Nullam semper urna sit amet risus auctor placerat. Nulla facilisi. Maecenas ut cursus purus. Vivamus mollis libero ac turpis venenatis id cursus nunc hendrerit. Sed nec nulla augue. Mauris orci felis, egestas commodo pulvinar id, mollis vitae est.</p>
    <p>Ut blandit venenatis arcu id euismod. Nunc faucibus, diam et euismod pretium, eros nulla pulvinar nibh, vitae ornare urna arcu eu orci. Vivamus sit amet lectus a dolor laoreet viverra. Donec et justo neque. Pellentesque eleifend justo in ante bibendum tempus. Sed pellentesque euismod faucibus.</p>
</div>

<h3>Example #3: Link (Content Source: URL - iframe)</h3>
<p>
    Clicking the link will open a Window, and that Window's content will be loaded into an iframe from some internal or external URL.
</p>
<p>
    <a id="haWindowTest3" class="window" 
        data-openOnLoad="false"
        data-openOnTimeoutInMilliseconds=""
        data-closeOnTimeoutInMilliseconds=""
        data-windowTitle="Example #3: Link (Content Source: URL - iframe)"
        data-contentSourceType="URL"
        data-contentSourceValue="http://www.lipsum.com/feed/html"
        data-additionalWindowClass=""
        data-height="500"
        data-width="800"
        data-maxHeight=""
        data-maxWidth=""
        data-minHeight=""
        data-minWidth=""
        data-allowCloseByEscKey="true"
        data-hideCloseByX="false"
        data-isDraggable="false"
        data-isModal="false"
        data-isResizable="true"
        data-position="center">Open Window (Content Source: External URL)!</a>
</p>

<h3>Example #4: Button (Content Source: Ajax + Ajax Form)</h3>
<p>
    Clicking the button will open a Window, and that Window's content will be loaded using an Ajax request. The content loaded will be an 
    <a href="../ajaxform/">Ajax Form</a>, which will allow an Ajax Post to be made to a service, a success action to be performed, and then 
    the Window will be automatically closed.
</p>
<div id="hdivAddPersonConfirmationMessage" class="confirmationMessageWrapper hide"></div>
<div id="hdivAddPersonErrorMessage" class="errorMessageWrapper hide"></div>
<p>
    <input type="button" value="Add Person" class="window buttonNormal" 
        data-openOnLoad="false"
        data-openOnTimeoutInMilliseconds=""
        data-closeOnTimeoutInMilliseconds=""
        data-windowTitle="Example #4: Add Person Ajax Form"
        data-contentSourceType="Ajax"
        data-contentSourceValue="/ui/library/window/AddPersonAjaxForm.aspx"
        data-additionalWindowClass=""
        data-height="430"
        data-width="500"
        data-maxHeight=""
        data-maxWidth=""
        data-minHeight=""
        data-minWidth=""
        data-allowCloseByEscKey="true"
        data-hideCloseByX="false"
        data-isDraggable="true"
        data-isModal="true"
        data-isResizable="true"
        data-position="['center','bottom']"/>
</p>

<h3>Example #5: Timing Event (Content Source: Inline HTML)</h3>
<p>
    Two Windows will automatically be opened when a timeout event expires, and the Windows' content will simply be the inline HTML within the requesting element. 
    The first Window will open three minutes after the page loads. The first Window will then automatically close after 30 seconds. Immediately following the close of 
    the first Window, a second Window will automatically open.
</p>
<pre>The code to automatically open this example's two Windows does not have<br />a visible presence on the page.</pre>
<div id="hdivWindowTest5_1" class="window" 
    data-openOnLoad="false"
    data-openOnTimeoutInMilliseconds="180000"
    data-closeOnTimeoutInMilliseconds="30000"
    data-windowTitle="Example #5: Session Warning"
    data-contentSourceType="InlineHTML"
    data-contentSourceValue=""
    data-additionalWindowClass=""
    data-height="200"
    data-width="400"
    data-maxHeight=""
    data-maxWidth=""
    data-minHeight=""
    data-minWidth=""
    data-allowCloseByEscKey="true"
    data-hideCloseByX="false"
    data-isDraggable="true"
    data-isModal="true"
    data-isResizable="true"
    data-position="center">
    Your session will expire in 30 seconds. Please finish what you are doing before your session times out.
</div>
<div id="hdivWindowTest5_2" class="window" 
    data-openOnLoad="false"
    data-openOnTimeoutInMilliseconds="210000"
    data-closeOnTimeoutInMilliseconds=""
    data-windowTitle="Example #5: Session Expired"
    data-contentSourceType="InlineHTML"
    data-contentSourceValue=""
    data-additionalWindowClass=""
    data-height="200"
    data-width="400"
    data-maxHeight=""
    data-maxWidth=""
    data-minHeight=""
    data-minWidth=""
    data-allowCloseByEscKey="false"
    data-hideCloseByX="true"
    data-isDraggable="false"
    data-isModal="true"
    data-isResizable="false"
    data-position="center">
    Your session has expired! I hope you did not loose any information you were working on. I tried to warn you. :-)
</div>

The Script

//Abstracted Methods for Window(s)
function openDialogWindow(requestingElement, isEventTrigger, isUserEvent) {
    var windowElementId;

    if (requestingElement.get(0).tagName == "DIV") {
        //Use the requesting element as the window element.
        windowElementId = requestingElement.attr("id");
    }
    else {
        //requestingElement is a link or button of sorts. Create new div for the window element.
        windowElementId = requestingElement.attr("id") + "_window";
    }

    if (isEventTrigger || requestingElement.attr("data-openOnLoad") == "true" || requestingElement.attr("data-initForOpenFromCodeRequest") == "true") {
        var windowElement;

        //Check to see if window element has already been initialized, or not.
        if ($("#" + windowElementId).is(":data(dialog)")) {
            //Already initialized.
            windowElement = $("#" + windowElementId);

            //Clear content from previous requests
            windowElement.html("");
        }
        else {
            //Does not yet exist.
            if (requestingElement.get(0).tagName == "DIV") {
                windowElement = requestingElement;
            }
            else {
                windowElement = $(divElementConstructor, { id: windowElementId });
            }

            windowElement.dialog({
                autoOpen: ((requestingElement.attr("data-openOnLoad") == "true") ? true : false),
                closeOnEscape: ((requestingElement.attr("data-allowCloseByEscKey") == "false" || requestingElement.attr("data-hideCloseByX") == "true") ? false : true),
                dialogClass: ((!isAttrDefined(requestingElement.attr("data-additionalWindowClass"))) ? "" : requestingElement.attr("data-additionalWindowClass")) + ((requestingElement.attr("data-hideCloseByX") == "true") ? " noClose" : "") + ((isAttrDefined(requestingElement.attr("data-contentSourceType")) && requestingElement.attr("data-contentSourceType").toLowerCase() == "url") ? " windowWithIframe" : ""), //add this CSS: .noClose .ui-dialog-titlebar-close { display:none; }
                draggable: ((requestingElement.attr("data-isDraggable") == "false") ? false : true),
                height: ((!isAttrDefined(requestingElement.attr("data-height"))) ? "auto" : ((isNaN(parseInt(requestingElement.attr("data-height"), 10))) ? requestingElement.attr("data-height") : eval(requestingElement.attr("data-height")))),
                width: ((!isAttrDefined(requestingElement.attr("data-width"))) ? 300 : eval(requestingElement.attr("data-width"))),
                maxHeight: ((!isAttrDefined(requestingElement.attr("data-maxHeight")) || requestingElement.attr("data-maxHeight") == "false") ? "false" : eval(requestingElement.attr("data-maxHeight"))),
                maxWidth: ((!isAttrDefined(requestingElement.attr("data-maxWidth")) || requestingElement.attr("data-maxWidth") == "false") ? "false" : eval(requestingElement.attr("data-maxWidth"))),
                minHeight: ((!isAttrDefined(requestingElement.attr("data-minHeight"))) ? 150 : eval(requestingElement.attr("data-minHeight"))),
                minWidth: ((!isAttrDefined(requestingElement.attr("data-minWidth"))) ? 150 : eval(requestingElement.attr("data-minWidth"))),
                modal: ((requestingElement.attr("data-isModal") == "false") ? false : true),
                position: ((!isAttrDefined(requestingElement.attr("data-position"))) ? "center" : ((requestingElement.attr("data-position").indexOf("[") >= 0) ? eval(requestingElement.attr("data-position")) : requestingElement.attr("data-position"))), //eval for array
                resizable: ((requestingElement.attr("data-isResizable") == "false") ? false : true),
                title: ((!isAttrDefined(requestingElement.attr("data-windowTitle"))) ? "No Title" : requestingElement.attr("data-windowTitle")),
                zIndex: ((!isAttrDefined(requestingElement.attr("data-zIndex"))) ? 1000 : eval(requestingElement.attr("data-zIndex")))
            });
        }

        //Add loading message when content is loaded from seperate HTTP request.
        if (isAttrDefined(requestingElement.attr("data-contentSourceType")) &&
                (requestingElement.attr("data-contentSourceType").toLowerCase() == "ajax" ||
                requestingElement.attr("data-contentSourceType").toLowerCase() == "url")) {
            windowElement.append($("#hdivWindowLoadingMessage").html().replace("**windowElementId**", windowElementId));
        }

        if (isEventTrigger) {
            windowElement.dialog("open");
            windowElement.dialog("moveToTop");
        }

        //Add CMS Attributes to Dialog Title
        if (windowElementId.indexOf("_cmsEditWindow") < 0) {
            //Don't do it for the CMS Edit window
            var windowTitleElement = $("#ui-dialog-title-" + windowElementId);
            windowTitleElement.attr("data-cmsKey", requestingElement.attr("data-cmsKey"));
            windowTitleElement.attr("data-activeCultureCode", requestingElement.attr("data-activeCultureCode"));
            windowTitleElement.attr("data-contentType", requestingElement.attr("data-contentType"));
        }

        if (isAttrDefined(requestingElement.attr("data-contentSourceType"))) {
            switch (requestingElement.attr("data-contentSourceType").toLowerCase()) {
                case "ajax":
                    var ajaxUrl = requestingElement.attr("data-contentSourceValue");
                    if (ajaxUrl.indexOf("?") > 0) {
                        ajaxUrl += "&";
                    }
                    else {
                        ajaxUrl += "?";
                    }
                    ajaxUrl += "windowId=" + windowElementId;

                    $.ajax({
                        type: "GET",
                        dataType: "html",
                        contentType: "application/x-www-form-urlencoded",
                        cache: false,
                        url: ajaxUrl,
                        success: function (data, textStatus, jqXHR) {
                            if (textStatus == "success") {
                                windowElement.html(data);
                            }
                            else {
                                errorOnAjaxFormPost(jqXHR, textStatus, "Error on Ajax Complete"); //Same errorOnAjaxFormPost used for the Ajax Form.
                            }
                        },
                        error: errorOnAjaxFormPost, //Same errorOnAjaxFormPost used for the Ajax Form.
                        complete: function (jqXHR, textStatus) {
                            if (textStatus == "success") {
                                enableUiFeatures("[aria-labelledby='ui-dialog-title-" + windowElementId + "']");
                            }
                        }
                    });
                    break;
                case "element":
                    windowElement.html($("#" + requestingElement.attr("data-contentSourceValue")).html());

                    self.setTimeout("enableUiFeatures(\"[aria-labelledby='ui-dialog-title-" + windowElementId + "']\")", 2);
                    break;
                case "inlinehtml":
                    //Do nothing.
                    break;
                case "url":
                    var iframeElement = document.createElement("iframe");
                    iframeElement.id = windowElementId + "_iframe";
                    iframeElement.setAttribute("src", requestingElement.attr("data-contentSourceValue"));
                    iframeElement.className = "windowIframe";
                    iframeElement.setAttribute("onload", "removeLoadingMessage(this);");
                    windowElement.append(iframeElement);
                    break;
            }
        }
    

The CSS

 
/* Window (Dialog) Stuff */
div.window { display:none; } /* if div, the intention is to load on page load or timeout */
.windowLoadingMessage { padding:60px 15px 1200px 15px; text-align:center; }
.noClose .ui-dialog-titlebar-close { display:none; }
.windowWithIframe { overflow-x:hidden; }
.windowIframe { width:100%; height:100%; margin:-3px 0px -3px 0px; overflow-x:hidden; }

<!-- The jQuery UI styling of your choice -->
<link href="jquery-ui.css" rel="stylesheet" type="text/css" />