reptile7's JavaScript blog
Tuesday, July 13, 2010
 
Linking Modality
Blog Entry #184

In the previous post, we created a custom dialog box for Microsoft's "execCommand Example: Creating a Link" demo, and we are now ready to put that box in front of the user. We know that the window.open( ) method - at least in its classical form - will not allow us to coordinate the user's URL input with the demo's createLink command. However, we have another card to play.

The return, part 2

For MSIE 4, Microsoft introduced two methods that extend the window.open( ) concept:
(1) window.showModalDialog( ), which was implemented in MSIE for Windows and Mac; and
(2) window.showModelessDialog( ), which was implemented in MSIE for Windows only.

Like window.open( ), window.showModalDialog( ) and window.showModelessDialog( ) both open a new, secondary browser window. The showModalDialog( ) and showModelessDialog( ) windows are meant for the facile transfer of information between themselves and their opener windows - that's the "dialog" part. The showModalDialog( ) window retains the input focus while open. The user cannot switch windows until the dialog box is closed, i.e., when a showModalDialog( ) window is created, the user is locked into a single mode of operation - that's the "modal" part. In contrast, when a showModelessDialog( ) window is created, the user is free to toggle back and forth between the opener and secondary windows.

Microsoft's showModalDialog( ) page at best suggests, but does not clearly state, that window.showModalDialog( ) causes the operating system to block; in practice, window.showModalDialog( ) does indeed block the operating system with most of the browsers that support it. Intuitively, you wouldn't think that window.showModelessDialog( ) would block the system; as this method isn't supported by any of the browsers on my iMac, a session at my local library was necessary to verify that...ah, I'll get to it one of these days.

You can test your browser's support for window.showModalDialog( ) here and for window.showModelessDialog( ) here.

modal=yes

Playing catch-up, Mozilla subsequently implemented in Netscape 6 a modal window.open( ) feature that when set to yes|1 locks focus on a secondary window but does not equip it with any dialog capabilities beyond those available to a normal secondary window. According to Mozilla, the modal feature has since Mozilla 1.2.1 required the UniversalBrowserWrite privilege (ugh). Netscape 6.2.3 and Netscape 7.02 are installed on my defunct G3 iMac's hard disk and are based on pre-1.2.1 versions of the Mozilla Application Suite, so I went into the SheepShaver environment to see if these browsers would unprivilegedly act on the modal=yes feature and give me a modal new window. Yes! Success! But as expected, modal=yes is ignored by all of the OS X browsers on my computer.

showModalDialog( ) cross-browserization, sort of

Mozilla began support for the showModalDialog( ) method with Firefox 3. Safari also now supports the showModalDialog( ) method, although I don't know when that support started. Firefox, Safari, and MSIE 5.x for the Mac all implement showModalDialog( ) as it should be implemented: with these browsers, the opened new window is in fact modal, and the calling showModalDialog( ) command does cause the operating system to block. FYI, my preferred way to test for system blocking is to immediately follow the showModalDialog( ) command with a document.bgColor = "green"; assignment: if the opener document background turns green when the new window pops up, it ain't blocking.

Chrome supports the showModalDialog( ) method, but buggily: the opened new window is not modal (focus can be shifted to the opener window by clicking on it), but at least the operating system blocks.

Camino's 'support' for showModalDialog( ) is even shakier: a modeless new window pops up and the operating system does not block.

Opera does not support the showModalDialog( ) command and throws a Type mismatch error in response thereto.

Dialog method syntax

The showModalDialog( ) and showModelessDialog( ) methods share a common syntax, e.g.:

vReturnValue = object.showModalDialog(sURL [, vArguments] [, sFeatures])

As for window.open( ), the first dialog method parameter, sURL, specifies the URL of the document that is loaded into the new window.

The second dialog method parameter, vArguments, is for passing information from the opener window to the new window (unlike the second window.open( ) parameter, it doesn't specify a name for targeting purposes).

As for window.open( ), the third dialog method parameter, sFeatures, specifies a string of new window features. Microsoft currently defines eleven sFeatures features; many of these features reflect corresponding window.open( ) features: dialogWidth and dialogHeight respectively parallel window.open( )'s width and height, scroll maps onto window.open( )'s scrollbars, etc. In contrast to the window.open( ) features string, in which features are assigned values conventionally via the = operator and in which feature/value pairs are delimited with commas, the dialog method features string has a CSS-like syntax in which the feature and value of an individual feature/value pair are separated by a colon and in which feature/value pairs are delimited with semicolons:

vReturnValue = window.showModalDialog("myDocument.html", "", "dialogWidth: 500px; dialogHeight: 150px; dialogLeft: 200px; dialogTop: 100px;");
vs.
oNewWindow = window.open("myDocument.html", "", "width=500,height=150,left=200,top=100");

Practical notes

• In theory, the second and third dialog method parameters are independently optional; in practice, I find that if the second parameter is left out - if it isn't at least set to an empty string - then the third parameter will be ignored and the new window will be given a default size and position, which varies somewhat from browser to browser.

• At least on my computer, the dialog method features string is case-insensitive, e.g., dialogWidth can be typed as dialogwidth.

• The px unit identifiers in the above showModalDialog( ) features string are actually unnecessary but their inclusion is recommended by Microsoft for consistent results. BTW, Microsoft states that dialogWidth and dialogHeight (and presumably also dialogLeft/dialogTop) can be specified in any of CSS's length units - em, ex, px, in, cm, mm, pt, or pc - I haven't tried all of these but I can verify that in and cm do not work with Firefox, Safari, and Chrome (they do work with MSIE 5.x); it is best to stick to px in this regard.

• As shown above, the dialog method features string can contain whitespace à la a CSS style rule set (also check out the Method Syntax Used: fields on the aforelinked showModalDialog( )/showModelessDialog( ) demo pages). In contrast, the window.open( ) features string is not supposed to contain whitespace (at least according to Mozilla), and perhaps this is true on a PC, but I can confirm that a "width=500, height=150, left=200, top=100"-type string is OK on a Mac.

vReturnValue

The window.showModalDialog( ) method returns the value of the new window's returnValue property, another part of the 'dialog interface' implemented by Microsoft in MSIE 4. Here's how the returnValue property would fit into the "Creating a Link" demo:

(1) A new window holding the custom dialog box is opened via a showModalDialog( ) command.

// In the opener document:
var linkURL = window.showModalDialog("customDialog.html", "", "dialogWidth: 468px; dialogHeight: 128px;");
...
document.execCommand("createLink", false, linkURL);


(2) The user enters a URL into the box's id="input0" text field and clicks the OK button. The latter action calls a getURL( ) function that assigns the user's URL to window.returnValue and then closes the new window. The window.returnValue value is transferred to linkURL, the showModalDialog( ) return, which in turn is fed to the createLink command to create a new link.
<!-- In the openee document: -->
<input type="text" id="input0" name="myURL" value="http://" />
...
<button type="button" id="b1" onclick="getURL( );">OK</button>
...
function getURL( ) {
	window.returnValue = document.getElementById("input0").value;
	window.close( ); }
The returnValue property is thus analogous to window.prompt( )'s second parameter, which makes sense, because deploying a high-end prompt( ) box is very much what we're doing here. Of the showModalDialog( )-supporting browsers on my computer (vide supra), returnValue is supported by MSIE, Firefox, Safari, and Chrome (in the opener document, Camino gives a null returnValue return, as though it were an object reference).

Unlike window.showModalDialog( ) but like window.open( ), the window.showModelessDialog( ) method returns an object reference for the new window that it opens. However, the first Example on Microsoft's showModelessDialog( ) page outlines a procedure for sending a value from a showModelessDialog( ) window to its opener window - a procedure that is equally viable for showModalDialog( ) windows. The key to this procedure is the window.dialogArguments property, which is also part of Microsoft's implemented-in-MSIE-4 dialog interface.

If we pass a reference/value to a dialog window via the second dialog method parameter, then that reference/value will be assigned to the dialogArguments property of the new window. For example, if we had a document with a <title>Welcome, Visitor</title> title and that popped up a window.showModelessDialog("newDocument.html", document.title); window, then a window.alert(window.dialogArguments); command in the newDocument.html source would pop up an alert( ) box bearing a Welcome, Visitor message.

Interestingly, and as illustrated in the aforementioned showModelessDialog( ) example, if we were to pass a window object reference to a dialog window via the second dialog method parameter, then the dialogArguments property will allow us to access global variables and to call non-nested functions in the opener document via the use of 'qualified names'. Microsoft provides here a demo page for the example. The original demo doesn't work for me as a Mac user; however, upon changing the showModelessDialog( ) call to a showModalDialog( ) call, I got the example to work with Safari, Chrome, and MSIE 5.1.6; upon subsequently changing the innerText assignment to a textContent assignment, I got it to work with Firefox.

Here's how we could apply the example to the "Creating a Link" demo:

(1) Globally declare linkURL as an empty string prior to the AddLink( ) function:

var linkURL = "";

function AddLink( ) { ... }


(2) Pop up a modal window holding the customDialog.html box with the following command:

window.showModalDialog("customDialog.html", window, "dialogWidth: 468px; dialogHeight: 128px;");

(3) In the customDialog.html source, recast the getURL( ) function as:
function getURL( ) {
	var window0 = window.dialogArguments;
	window0.linkURL = document.getElementById("input0").value;
	window.close( ); }
In the above function, linkURL in effect functions as a property of the opener (window0) window. Indeed, for Firefox, Safari, and Chrome, I find that the window0 lines, and therefore the use of the dialogArguments property, can be replaced by an opener.linkURL = document.getElementById("input0").value; assignment (this works with Camino too but it throws an 'opener' is not an object runtime error with MSIE).

Demo

The div below holds a slightly modified version of the "Creating a Link" demo appearing at the end of Blog Entry #182. Upon selecting some second paragraph text and clicking the Click to add link button:
(a-d) MSIE, Firefox, Safari, and Chrome will pop up my custom dialog box, whose OK button uses the returnValue property to send the user's URL to the opener document;
(e-f) Opera and Camino will pop up the prompt( ) box of the Blog Entry #182 demo via the following conditional:
if (window.opera || navigator.userAgent.indexOf("Camino") != -1)
	var linkURL = window.prompt("Please enter your link URL into the field below.", "http://");
Select any portion of the following blue text, such as "My favorite Web site". Click the button to turn the selected text into a link.

My favorite Web site is worth clicking on. Don't forget to check out my favorite music group!


Here's the dialog box that appears in the window:

Hyperlink
Hyperlink Information


October 2016 update notes
• Firefox 48 will apply the foreColor execCommand( ) operation to non-link text but not to link text.
• Google Chrome 49 throws an Uncaught TypeError: window.showModalDialog is not a function when it hits the showModalDialog( ) command.
• Opera 23 does not support the window.opera object but does support the showModalDialog( ) method.
• On its window.showModalDialog( ) page Mozilla warns that the showModalDialog( ) method has been removed from the Web standards and is going away, and therefore my demo may not work for you if you are using a current browser. We should really be doing this with window.open( ) and I've thought up a way to get around window.open( )'s non-blocking problem but that's another post for another time.

And that will conclude our long and winding discourse on the execCommand( ) method. I was originally going to append to this post a section on the Microsoft Copy code that was briefly discussed at the beginning of Blog Entry #178, but it turns out that this code doesn't actually make use of the execCommand( ) method (it's based on a proprietary clipboardData object), so maybe we should just move on, huh? Accordingly, then, we will in the following entry check over the next Beyond HTML : JavaScript tutorial, "So You Want A Cookie Counter, Huh?" - we've done the cookie script thing a couple of times previously so we should be able to knock this one off in one post.

reptile7

Comments:
I find that "showModelessDialog" is brilliant for creating "progress bars", and it is a shame that this potential usage has not been noted.

The dialog runs in a separate thread (I think) so that you can update its contents to reflect the progress of a javascript routine.

Also, being able to pass dialogArguments means that the user can communicate cancel requests back to the main javascript routine.
 
Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)