jQuery UI Dialogs & Sharepoint UI Dialogs 1

Mike BerrymanI was recently presented with a challenge that I was surprised is not more common (at least, if my google searches are to be believed).  Essentially, I had a situation where the client was using the Sharepoint UI Dialog to pop up a custom edit form for a list item, and in that dialog they wanted to pop up another dialog to show a list of documents the user could select from to attach to the list item they’re editing.  I opted to use jQuery UI to create this second dialog, which worked great…at first.  The snag came when it was revealed that the second dialog needed to be much, much larger than the originating dialog – meaning it could not live within the Sharepoint UI dialog.

A little background before we continue.  The jQuery UI dialogs and the Sharepoint UI dialogs work in very fundamentally different ways.  I won’t go into the specifics on how exactly to use each dialog (there are tutorials all over the place on the web), but for the purpose of this blog post there are some difference I need to touch on that aren’t immediately obvious from just knowing how to use the two dialog types.

Once you initialize a jQuery UI dialog on a div, the DOM is actually manipulated such that the original div is wrapped in a series of divs and moved to the bottom of the DOM.  There’re a lot of other things jQuery UI does to turn this div into a dialog, but for our purposes we only care about this particular piece of manipulation.  As a side note, moving the dialog to the bottom of the DOM has the nasty side effect of removing the dialog from the form, which effectively makes any server-side controls in the dialog inaccessible from server-side code.  This is easy to fix, however, with the following snippet:

$("<Your dialog selector>").parent().appendTo("form:first");

This moves your dialog (freshly wrapped in a series of divs from the dialog initialization) back inside the form, making your server-side control accessible again.  This is an important concept, since it shows that the dialog does not have to reside where the initialization puts it – a piece of information that is useful later.  The thing to note about jQuery UI dialogs is that they originally were and still are elements that live inside the page’s DOM.

Sharepoint UI dialogs differ from jQuery UI dialogs in that they are actually iFrames wrapped up in a div that functions similarly to jQuery UI in that the dialog itself is a div that lives on the page and has dialog-like functionality (can be resized, dragged around, etc).  The key difference is that the content in the dialog is actually an iFrame, and thus resides on a completely different page.  Astute readers will note that because this is an iFrame, it’s possible (however unlikely in the context of sharepoint) that the page within the iFrame lives in a different domain.  If that’s the case, this solution will not work due to the limitations of cross-domain scripting.  Technically it’d still be possible, but that’s a post for another day.

Knowing this, it makes sense that when a jQuery UI dialog is popped up from a Sharepoint UI dialog, it cannot be dragged outside of the Sharepoint dialog, or even resized to be bigger than it.  The jQuery UI dialog is originating from an iFrame and is constrained by the iFrame itself.

So what’s the solution?  We’ll do some DOM manipulation of our own!

The general idea of this solution is to move the jQuery dialog from out of the iFrame, out of the Sharepoint dialog, and onto the original page that launched the Sharepoint dialog in the first place.  This allows the jQuery dialog to exist outside the confines of the Sharepoint dialog with all the content that was originally generated within the Sharepoint dialog but free to move about on the whole screen and even resize to larger than the Sharepoint dialog.  In this particular case, I’m also going to move the jQuery dialog back into the Sharepoint dialog once the user is finished with it so that the Sharepoint dialog (remember, it’s really just an iFrame) regains access to any controls within the jQuery dialog.

var dialogSelector = "#divToBecomeADialog";
$(document).ready(function () {
    //remove any orphaned dialogs
    parent.$("div.ui-dialog").remove();
    parent.$(dialogSelector).remove();
    SetUpjQueryDialog();
});

function SetUpjQueryDialog() {
    var body = parent.$("body");
    $(dialogSelector).appendTo($("form:first", body));
    parent.$(dialogSelector).dialog({
        autoOpen: false,
        modal: true,
        dragable: false,
        width: 800,
        height: 600,
        zIndex: 9999,
        resizable: false,
        title: "Dialog Title",
        close: function () {
            var spDialog = parent.$("div.ms-dlgContent");
            var frame = $("iframe.ms-dlgFrame", spDialog);
            var form = $(frame).contents().find("form:first");
            $(this).parent().appendTo(form);
        },
        buttons: {
            "Save": function () {
                parent.$(this).dialog("close");
            },
            "Cancel": function () {
                parent.$(this).dialog("close");
            }
        }
    });
}

function OpenjQueryDialog() {
    var dlg = parent.$(dialogSelector);
    if (dlg.length == 0) {
        var body = window.parent.$("body");
        $(dialogSelector).parent().appendTo($("form:first", body));
        dlg = parent.$(dialogSelector);
    }
    parent.$(dlg).dialog("open");
}

This script lives within the Sharepoint dialog, which is to say it originates from the page loaded into the iFrame.  It performs functions on both the originating page and the parent page (the page that launched the Sharepoint dialog), hence we need to switch contexts using the ‘parent’ qualifier.  You can see that right off the bat in the $(document).ready() function the parent is checked to see if it has any orphaned dialogs since it’s possible that the user could close the Sharepoint dialog before it has a chance to pull the jQuery dialog back into the iFrame.  If that’s the case, the next time the Sharepoint dialog is loaded (either a fresh load or a postback), the orphaned versions of the jQuery dialog will be dealt with.

As for the actual jQuery dialog itself, you can notice that the first thing that happens in the SetUpjQueryDialog() function is that we get a reference to the body tag of the parent.  Before the div that’s going to be turned into a jQuery dialog is even initialized into a dialog, it is moved out of the iFrame and into the parent.  This is so that when it does get initialized, all the additional elements that are generated at the same time stay with the dialog.  You’ll also notice the dialog initialization is being performed in the parent context.  Since the div we’re transforming into a dialog has been moved out of the iFrame, but the javascript still lives within the iFrame, anything being done to the dialog from this point forward has to be prepended with the parent. qualifier, so the script runs in the context of the parent page.

To open the jQuery dialog now that it lives outside of the iFrame, we need to again run some javascript from one page in the context of another.  Other than making sure the context is correct, it’s the standard syntax to open the jQuery dialog.  You’ll notice is my example, I’m also making sure that if the dialog was not moved outside of the iFrame, it’ll move it then open it.

The next interesting piece of code is the close function for the dialog.  I set it up such that once the dialog is closed (no matter what the method of closing the dialog was), it is moved back into the original iFrame page.  This is done by looking for a div with the class of ms-dlgContent within the parent page – this is the containing div for the Sharepoint dialog.  Within that div, we can find the iFrame with a class of ms-dlgFrame – this is the content of the Sharepoint dialog.  In this particular example, I’m also making sure the jQuery dialog is being put back into the form element so that my server-side code can access any server controls within the dialog.  This isn’t purely necessary, but it doesn’t hurt if you don’t have any server controls in the dialog so there really isn’t any reason not to.  The thing to remember here is that the close function happens immediately, even if called programmatically.  Hence, if you wish to access any controls that would trigger some server-side processing (such as a hidden button on the jQuery dialog that triggers an event), the dialog must be closed first so it’s put back into its original context.

That’s it!  Once you wrap your head around the fact that this dialog is moving between two different pages and thus, two different contexts, it’s actually a fairly nice solution.  I’m sure it can be improved upon, but this bare-bones template gives all the functionality necessary to move a jQuery dialog out of a Sharepoint dialog then back into it once finished.

One comment

  1. I’m looking for a UI expert has developed UI on top of SharePoint. The ability to design and implement workflows that run on SharerPoint is a plus.

    Very competitive salary. Work is full-time in Pasadena, California.
    Thank you.

    Steve

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s