reptile7's JavaScript blog
Tuesday, May 18, 2010
 
Executorship of Command
Blog Entry #179

We continue today our discussion of HTML Goodies' "Click... It's Copied!" tutorial and its ClipBoard( ) function that uses the execCommand( ) method to copy a text string to the operating system's clipboard.

More on execCommand( )

Let's begin by fleshing execCommand( ) out a bit more. Per its name, the execCommand( ) method can execute various commands on a document or a portion thereof. As noted in the previous entry, the execCommand( ) method was implemented by Microsoft in MSIE 4 for Windows. Microsoft applied execCommand( ) initially to the document and TextRange objects and later to a "controlRange collection" (whose sketchy documentation and nonsupport on my iMac have prevented me from figuring out just what it is, so we're not gonna discuss it). Subsequently, execCommand( ) was picked up for Firefox and other browsers, which, as far as I am aware, only support it for the document object. Moreover, the W3C is bringing document.execCommand( ) into HTML5.

Microsoft posts here a "Command Identifiers" page with a list of supported (and not currently supported) execCommand( ) commands; Mozilla lists a somewhat-differing set of supported execCommand( ) commands here. Most of these commands change a document in some way, for example, by formatting text or inserting new elements, although a few do not. At least on the Mozilla side, execCommand( ) is meant to be used in conjunction with either
(a) the designMode property of the document object, which must be set to on, or
(b) the contentEditable attribute/property, which can be applied to most elements/objects and must be set to true,
and this might be the case on the Microsoft side as well, even though the MSDN Library's execCommand( ) page and its document.execCommand("CreateLink"); Example mention neither designMode nor contentEditable.

Before we go any further, I should clear up something I said at the end of the previous post, namely, that document.execCommand( ) is supported by MSIE 5.x for Mac OS X: this is true 'in theory' but not in practice. In response to the conditional below

if (document.execCommand) window.alert("Yes");
else window.alert("No");


the three versions of MSIE 5.x on my computer - MSIE 5.2.3 (OS X), MSIE 5.1.6 (Classic), and MSIE 5 (Classic) - all pop up the Yes alert( ) message*; in addition, they return function execCommand() { [native code] } for document.execCommand itself. Sounds like we're good to go, eh? However, my attempts to run actual document.execCommand( ) commands with these browsers have been uniformly unsuccessful.

*MSIE 4.5 throws an Object doesn't support this property or method error so I guess that means No. BTW, the MSIE 5.x browsers all return undefined for both document.designMode and document.body.contentEditable.

Another aside:
Support for the designMode property can be reliably assessed with an analogous
if (document.designMode) window.alert("Yes"); else window.alert("No"); statement
but the same is not always true for the contentEditable property via an
if (document.body.contentEditable) window.alert("Yes"); else window.alert("No"); statement.
Regarding the latter conditional:
• Firefox and Camino return inherit for document.body.contentEditable and thus pop up the Yes alert( ) message.
• Safari and Chrome return false as a string for document.body.contentEditable. As an if condition, a false string converts to true and therefore these browsers pop up the Yes alert( ) message.
• Opera returns false as a boolean for document.body.contentEditable and thus pops up the No alert( ) message.

In any case, document.execCommand( ), designMode, and contentEditable are supported by all of the other (non-MSIE) OS X GUI browsers on my computer. Setting document.designMode to on or equipping the body element start-tag with a contenteditable="true" attribute turns a page into one large editable field in which a user can add and edit content in various ways, somewhat like using a word processor. Manipulating such a page with document.execCommand( ) commands is easier said than done, however.

The Remarks section of the aforecited MSDN Library execCommand( ) page notes, Do not invoke the execCommand method until after the page loads. I can confirm that execCommand( ) commands have no effect if they are run as top-level code in a script element in the document head; rather, they are meant to be fired via "widgets" (user interface elements) of some sort - typically by clicking push buttons, although any mouse/keyboard-type actions on relevant elements could be used in this regard. In setting up an execCommand( ) field, it is in practice desirable to separate the interface for manipulating the field from the field itself for at least two reasons:
(1) Widgets that are part of an editable field can be deleted or otherwise edited by the user - not good, obviously.
(2) Most execCommand( ) commands act at a mouse-cursor position or selection. If a widget is part of an editable field, its use will with some browsers (notably Safari) withdraw focus from the position or selection at which the widget's associated execCommand( ) command is meant to act: loss of focus = no command effect.

One approach to achieving editable field/interface separation is to load the editable field document into an iframe and then place the execCommand( ) widgets in the iframe's host page - for an example thereof, check out Mozilla's rich-text editing demo. And if you're using designMode to make the iframe src page editable, this approach will for Firefox, Opera, and Camino circumvent a peculiar designMode aspect noted here by Mozilla: [O]nce a document is switched to designMode, all [event-triggered JavaScript commands in] that particular document are disabled (at least on my computer, this quirk is not exhibited by Safari and Chrome).

Alternatively, a simpler approach to field/interface separation is illustrated by Mark Finkle's contentEditable demo: instead of using an entire page for an editable field, code a large div element equipped with a contenteditable="true" attribute, and then place the execCommand( ) widgets outside (before or after) the div element.

Syntax

Before I roll out my own demo, we should probably discuss the execCommand( ) syntax a bit. Depending on the command and depending on the browser, the execCommand( ) method can have one, two, or three parameters:

(1) The first execCommand( ) parameter is a case-insensitive string specifying the command to be carried out: copy, indent, justifyRight, etc.

(2) There are four execCommand( ) commands - createLink, insertImage, print, and saveAs - that require or can optionally deploy an associated user interface, more specifically, a dialog box with which the user interacts in some way; for example, the image-inserting insertImage command can pop up a prompt( )-like box asking for a URL path to an image the user wants to insert. The second execCommand( ) parameter is a boolean switch for activating (true) or disabling (false) this interface.

(3) Many execCommand( ) commands can or must act on a value; these values are set out in the third execCommand( ) parameter (although sometimes they are solicited from the user, as noted above).

The Syntax box on the MSDN Library's execCommand( ) page shows the second and third execCommand( ) parameters to be optional. Microsoft's "Command Identifiers" page links to separate pages for each execCommand( ) command; going through the individual command pages reveals that:
• If a given command doesn't act on a value or if its value is optional, then the third execCommand( ) parameter can be omitted, but if a command requires a value, then the third execCommand( ) parameter must be specified.
• The second execCommand( ) parameter must be set to false for commands that require values but is optional in all other cases; for commands to which it is relevant, this parameter defaults to true if it is omitted.

On the Mozilla side, all three parameters must be specified for all execCommand( ) commands (see point #7 on this page) even though the second execCommand( ) parameter is not implemented in Mozilla and should thus be set to false in all cases (you can also set it to null - check the source of the aforelinked contentEditable demo). Mozilla's execCommand( ) commands either require values or don't take them at all - for the latter cases, the third execCommand( ) parameter should be set to null.

We can clarify the above with a few examples:

(1) The text-bolding bold command doesn't have an associated user interface and doesn't take a value, required or optional. On the Microsoft side, we can simply write
document.execCommand("bold");,
but on the Mozilla side, we have to write
document.execCommand("bold", false, null);.

(2) The text-coloring foreColor command doesn't have an associated user interface but does require a value, specifically, an RGB hex code or a recognized color name. If we want to create some red text with foreColor, then both Microsoft and Mozilla require us to write
document.execCommand("foreColor", false, "#ff0000"); // Or just "red" for the third parameter.

(3) For the aforediscussed insertImage command, Microsoft gives us the option of dialoguing the user for an image URL via
document.execCommand("insertImage"); /* You can include a true second parameter but it's not necessary. */
or hard-coding an image URL into the source via
document.execCommand("insertImage", false, "/images/myImage.gif");,
whereas Mozilla only grants us the latter option.

(4) The no-interface insertHorizontalRule command generates a new hr element. Microsoft allows us to equip the hr element with an id attribute value via an optional third execCommand( ) parameter string, i.e.,
document.execCommand("insertHorizontalRule", false, "hr1"); /* The second parameter can be omitted in this case. */
produces
<hr id="hr1">;
however, if we don't want or care about an id value, then
document.execCommand("insertHorizontalRule");
is all we need. Mozilla's insertHorizontalRule doesn't take a value** and thus we are left with
document.execCommand("insertHorizontalRule", false, null);.

**Contradicting other Mozilla materials, Mozilla's "Midas" page claims that insertHorizontalRule can indeed take an id value-providing argument. In practice on my computer, Firefox and Camino turn
document.execCommand("insertHorizontalRule", false, "hr1");
into an id-lacking
<hr style="width: 100%; height: 2px;">.

I find that Safari, Opera, and Chrome generally follow the Microsoft syntax model: only the first execCommand( ) parameter is necessary for a command that doesn't take a value but all three parameters are needed for a command that requires a value. On the other hand, and as you would expect, Firefox and Camino follow the Mozilla syntax model, i.e., all three parameters are required in all cases.

One last point regarding the Mozilla syntax model:
On its "Midas Specification" page, Mozilla states, If [the second execCommand( ) parameter] is set to true, you will get an error (NS_ERROR_NOT_IMPLEMENTED). When using Firefox, I do see this error with, say,
document.queryCommandSupported("print");
(we'll discuss the queryCommandSupported( ) method in the following entry), but not with, say,
document.execCommand("createLink", true, null);,
which does nothing at all: a dialog box does not pop up and no errors are thrown.

Demo

We'll get to this first thing next time.

reptile7

Comments: Post a Comment

<< Home

Powered by Blogger

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