reptile7's JavaScript blog
Wednesday, June 30, 2010
 
Custom-Built for Linking
Blog Entry #183

Today we will address the use of a custom dialog box to solicit a link URL from the user for Microsoft's "execCommand Example: Creating a Link" demo. In the sections below, we'll craft such a box from scratch and then integrate it with the non-MSIE demo code that we developed in the previous entry, with a separate demo to follow.

The box

Our custom dialog box will be modeled on Microsoft's own document.execCommand("createLink"); "Hyperlink" dialog box and will look like this:

[Our custom dialog box]

The library blues

I walked over to my local library hoping to obtain a screen shot file of the Microsoft "Hyperlink" box, but it was not to be:

(1) I took an Alt-PrtSc screen shot of the "Hyperlink" box and could paste it into a Microsoft Word window, but was unable to save it to the desktop because the Save commands of the Word version on the library's computers have somehow been disabled.

(2) Neither Microsoft Paint nor any other graphics editor is installed on the library's computers. (Application-wise, these computers are stripped rather cleanly vis-à-vis a normal PC.)

(3) The "Hyperlink" box screen shot would not paste into a Gmail "Compose Mail" message field.

Arrggghhhh!!!!! I tried. But I was at least able to write down enough information about the box for us to move forward...

Basic structure and functionality

Microsoft's "Hyperlink" box basically comprises a set of four controls:
(1) a selection list for choosing a URL scheme;
(2) a text field for holding the link URL;
(3) an OK button for sending the link URL to the opener document; and
(4) a Cancel button.

The selection list contains ten options, in source order:
(other), file:, ftp:, gopher:, http:, https:, mailto:, news:, telnet:, wais:
The http: option is selected initially.



<select id="select0" onchange="getScheme(this);">
<option value="">(other)</option>
<option value="file://">file:</option>
...
<option value="http://" selected="selected">http:</option>
...
<option value="telnet://">telnet:</option>
<option value="wais://">wais:</option>
</select>


The option values can be loaded into the text field

<input type="text" id="input0" name="myURL" value="http://" />

via the following getScheme( ) function:
function getScheme(mySelect) {
	var sI = mySelect.selectedIndex;
	var oV = mySelect.options[sI].value;
	document.getElementById("input0").value = oV; }
Of course, for entering a relative URL into the field, the user is free to delete the preloaded scheme.

We will detail the action of the OK button

<button type="button" id="b1" onclick="getURL( );">OK</button>

in the "The return" section below. As for the Cancel button, it must at least close the box, but you also might want it to pop up a message for the user:
<button type="button" id="b2" onclick="ifClose( );">Cancel</button>
...
function ifClose( ) {
	window.alert("You can't create a link if you don't input a URL.");
	window.close( ); }
Layout and related extras

The Type: and URL: captions that respectively precede the selection list and text field are semantically labels, so let's mark them up that way. Moreover, the selection list and text field appear to be 'structured' with fieldset and legend elements, so let's add those guys too:

#fieldset0 { float: left; height: 65px; }
...
<fieldset id="fieldset0">
<legend>Hyperlink Information</legend>
<label for="select0">Type:</label> <select id="select0" onchange="getScheme(this);"> ... </select>
<label for="input0">URL:</label> <input type="text" id="input0" name="myURL" value="http://" />
</fieldset>


The fieldset must be floated left in order to flow the OK and Cancel buttons down its right side.

Let's give all the controls specific widths - this doesn't guarantee that they'll have the same size across browsers, but it helps:

#select0 { width: 70px; }
#input0 { width: 300px; }
#b1, #b2 { width: 60px; }


The field of a conventional prompt( ) box will hold, depending on the browser, 30-40 characters. Giving the input0 control a size="40" attribute does not produce a uniform-width field, FYI.

To make the OK and Cancel buttons look more like 'real' dialog box buttons, I've given them both outset border-styles and have also slightly thickened the OK button border-width. (Check out HTML Goodies' indispensable "CSS and Borders" tutorial, which is illustrated with various CSS borders.) And true to the Aqua GUI, the OK and Cancel buttons have respectively been given bluish and off-white background-colors - interestingly, doing this causes their shapes to change from oval to rectangular - they're a dull gray in Microsoft's original box, if I recall correctly:

#b1 { border: outset 4px; background-color: #cae3fd; }
#b2 { border: outset; background-color: #fefefe; }


I've shifted the buttons rightward and downward a bit, per my preference:

#b1, #b2 { margin-left: 8px; margin-top: 8px; }

The Microsoft title bar, including its close button, can be reproduced (more or less) by the following div element:

#div1 { background-color: blue; color: white; }
#b0 { position: relative; left: 350px; background-color: #fefefe; }
...
<div id="div1">Hyperlink <button type="button" id="b0" onclick="ifClose( );">X</button></div>


Finally, the box itself is framed by an outer div element:

#div0 { width: 450px; height: 110px; border: 1px solid black; }
...
<div id="div0"> ... </div>


Key effects

If you want the pressing of the enter/return key to simulate the clicking of the OK button, then that's pretty easy to do:
function keyEffects(e) {
	/* I don't know if there are any non-MSIE browsers out there that only support Microsoft's event model, but just to be on the safe side... */
	var thisKey = e ? e.which : (event ? event.keyCode : ""); // See Blog Entry #143 for more on this conditional.
	if (thisKey == 13) getURL( ); } // 13 is the decimal Unicode code position of a carriage return.
document.onkeypress = keyEffects;
Now, if we really wanted to be Windows nerds, we would include code that directs focus to the selection list and to the text field upon typing (shift-)alt-T and (shift-)alt-U, respectively, per the underlined T and U in the Type:/URL: labels; my attempts to do so have been only partially successful, however. On a Mac, the option/alt key is inter alia used to generate non-keyboard characters. Typing option-T generates a (dagger) character, whose decimal Unicode code position is 8224, whereas typing shift-option-T generates a ˇ caron phonetic modifier, whose decimal Unicode code position is 711. It follows that a

if (thisKey == 8224 || thisKey == 711) document.getElementById("select0").focus( );

conditional should bring focus to the selection list in response to the option-T or shift-option-T key combination, and this is indeed the case.

The decimal Unicode code positions for a lowercase t and an uppercase T are 116 and 84, respectively. However, an

if ((thisKey == 116 && e.altKey) || (thisKey == 84 && e.altKey)) { ... }-type

conditional cannot be used to flag the option-T and shift-option-T key combinations on a Mac, although this might work on the Windows side, I don't know.

The Mac uses the option-U and shift-option-U key combinations to place umlauts over vowels. The decimal Unicode code position for an umlaut is 168, which is the thisKey value for the shift-option-U key combination but not for the option-U key combination. Weirdly, none of the OS X GUI browsers on my computer detects a keypress event at all upon typing option-U; without getting into the details, switching from onkeypress to onkeydown or onkeyup did not help. Still, I see no harm in adding
if (thisKey == 168) document.getElementById("input0").focus( ); and
if ((thisKey == 117 && e.altKey) || (thisKey == 85 && e.altKey)) document.getElementById("input0").focus( );
conditionals to the keyEffects( ) function.

BTW, my preferred way to underline the T and U is to wrap the Type: label and selection list in a <div id="div2" class="fl"> ... </div> element and to wrap the URL: label and text field in a <div id="div3" class="fl"> ... </div> element and then give these div elements a .fl:first-letter { text-decoration: underline; } style. (The :first-letter pseudo-element cannot be applied directly to the label element, at least not without giving the latter a non-inline display.)

The return, part 1

Popping up our custom dialog box is accomplished classically via the window.open( ) method:

var newWindow = window.open("customDialog.html", "", "width=468,height=128");

The window.open( ) method returns an object reference for the new window that it opens, newWindow in this case. If the preceding command caused the operating system to block - i.e., if the computer just stopped and waited for the user to
(a) enter a URL into the custom dialog box's text field and
(b) click the OK button
before moving on to the rest of the script - then a

var linkURL = newWindow.document.getElementById("input0").value;

assignment could be used to grab the user's URL for the createLink command; alternatively, if we were to append an

<input type="hidden" id="input0" name="myURL" />

control to the opener document source, then a

opener.document.getElementById("input0").value = document.getElementById("input0").value;

customDialog.html command would similarly send the user's URL to the opener document, where it could be picked up by the AddLink( ) function via a

var linkURL = document.getElementById("input0").value;

assignment. But as discussed in the previous post, window.open( ) does not block the operating system; as a result, the user's URL does not get to the opener document in time for the createLink command to act on it. What to do? Tune in to our next episode for a not-quite-cross-browser solution to this problem.

reptile7

Monday, June 21, 2010
 
Linking Down Two Sides
Blog Entry #182

We continue today our discussion of Microsoft's "execCommand Example: Creating a Link" demo. We deconstructed the demo's code in the previous entry and are now ready to adapt the demo to non-MSIE browsers; in doing so, there are three basic issues that we need to address:
(1) How can we verify that the user has selected some text?
(2) For browsers that don't support the second execCommand( ) parameter - and that's all of them on the Mac platform - how do we query the user for a link URL?
(3) How do we confirm that link creation has been successful?
Of these issues, the second is the 'lowest-hanging fruit', so we'll deal with it first.

Your URL, comrade

Point #6 of Mozilla's "Converting an app using document.designMode from IE to Mozilla" article notes a simple and obvious way to solicit a link URL from the user: use a prompt( ) box.

var linkURL = window.prompt("Please enter your link URL into the field below.", "http://");

['Enter the URL' prompt( ) box]

The prompt( ) return, linkURL in this case, can then be fed to the createLink command:

document.execCommand("createLink", false, linkURL);

"But I want a box like Microsoft's box."

Well, in that case you're going to have to create your own custom dialog box, and that's not difficult to do - the basic structure of the box is easy to code, although right-aligning the and buttons takes a bit of CSS patience - but integrating that box with the rest of the script is easier said than done. A window.open( ) command can be used to pop up such a box, and an inputted URL can be returned to the opener document by, say, assigning it to the value of an <input type="hidden"> control. But unlike the prompt( ) method, window.open( ) does not cause the operating system to block (as in to delay or sit idle while waiting for something): the browser will speedily move on to the createLink command and then either create a link to undefined (Firefox, Opera, Camino) or do nothing at all (Safari, Chrome) because there's no URL value to act on (yet).

However, Microsoft has more recently implemented a showModalDialog( ) method and a returnValue property, both of the window object and now supported to varying degrees by Firefox, Safari, Camino, and Chrome, that in theory would allow us to get around the window.open( ) non-blocking problem; indeed, Mozilla has posted here a showModalDialog( )/returnValue demo that does just what we want to do in the present situation. Notwithstanding the broken link in its dialog box, the Mozilla demo works very nicely with Firefox, Safari, and Chrome - it'd also work with MSIE 5.x for the Mac if the latter supported the textContent property - but not so well with Camino, which does open the showModalDialogBox.html window but doesn't system-block at the showModalDialog( ) command in the opener source (the alert(r); command fires, popping up a null message). Oddly, Opera doesn't support the showModalDialog( ) method at all.

Now, I really should flesh out this custom dialog business to a greater extent at this point - particularly so given that the W3C is bringing the showModalDialog( ) method and the returnValue property into HTML5 (nope, these features didn't make it to the HTML5 Recommendation stage) - but as I wanted to concentrate on cross-browser code in this entry, let's just go with the prompt( ) route for the time being.

Are you selected?

Netscape may have begun its approach to selection access with the humble document.getSelection( ) method, but that's certainly not where Mozilla is today in this area. Shortly after the implementation of Microsoft's document.selection object, Netscape/Mozilla developed its own selection object, which was introduced in Netscape 6 - here are the details:

(1) getSelection( ) was moved from the document object to the window object.
(document.getSelection( ) still appears in Mozilla's list of document object methods but there's no separate page for it and Mozilla now maintains a skeletal page for it here. document.getSelection( ) is supported by almost all of the non-MSIE GUI browsers on my computer although it generates a Deprecated method document.getSelection() called. Please use window.getSelection() instead. Error Console message when using Firefox and Camino.)

var selObj = window.getSelection( );

(2) window.getSelection( ) returns not a string but an object implementing a Selection interface comprising a much larger set of properties and methods than that given to Microsoft's selection object. There's no overlap between the Mozilla and Microsoft selection objects: their respective sets of properties and methods are mutually exclusive.

(3) The Mozilla selection object can be variabilized with any valid identifier, including selection, which is not a JavaScript reserved word.

(4) The Mozilla Selection interface has a toString( ) method that [r]eturns a string currently being represented by the selection object, i.e., the currently selected text; the JavaScript engine calls this method automatically when a selection object is used in a context requiring a string. Fortunately, it is not necessary to create a separate Range object to access selection text as it is in Microsoft's case.

Mozilla synopsizes its Selection interface in a DOM selection Reference that is part of the Gecko DOM Reference. The W3C will be bringing the Mozilla Selection interface into HTML5 a Selection API specification.

Getting back to the "Creating a Link" demo, we would therefore use the following Mozilla code to determine whether the user has made a selection or not:
var selObj = window.getSelection( );
if (selObj != "") { // If the user has selected some text:
	...create a link and color its text... }
else window.alert("Please select some text!");
/* In the if condition, selObj stringifies to the selection text; it is not necessary to explicitly call selObj.toString( ), as noted above. */
I am gratified to report that Firefox, Opera, Safari, Camino, and Chrome all support the above code.

Anchor element A-OK

Following Microsoft's design, here's how we might on the Mozilla side test if the createLink command was successfully executed:

Mozilla's Selection interface has a getRangeAt( ) method that can be used to create a standard Range object representing the selection text.

var range = selObj.getRangeAt(0); /* range isn't a JavaScript reserved word either. */

A selection can encompass one or more ranges, which for the getRangeAt( ) method are indexed ordinally à la other script collections. Our My favorite Web site selection holds a single range whose getRangeAt( ) index is 0.

The standard Range object does not have a method corresponding to Microsoft's parentElement( ) method; however, it does have a commonAncestorContainer property that [r]eturns the deepest Node that...encloses a Range:

var rangeAncestor = range.commonAncestorContainer; /* Returns [object Text] in our case */

For My favorite Web site, rangeAncestor would return/point to a Text Node, more specifically, an object implementing the DOM Text interface and representing the My favorite Web site is worth clicking on. Don't forget to check out my favorite music group! text of the demo's second paragraph.

The Text interface inherits from the DOM Node interface a parentNode property that when applied to an element's text returns a Node object corresponding to the element and implementing the DOM Element interface. With an Element Node in hand, we can use the tagName property to check if My favorite Web site now has an anchor element parent:

if (rangeAncestor.parentNode.tagName == "A") { ...color the link text or take some other action... }
else window.alert("Sorry, we are unable to determine whether link creation was successful or not.");


Additional comments

• The range range is bounded by a single (text) node. The standard Range object's startContainer property, which [r]eturns the Node within which the Range starts, or its endContainer property, which [r]eturns the Node within which the Range ends, can therefore be used instead of the commonAncestorContainer property if desired.

• For the text of a link, a linkTextNode.parentNode expression stringifies to the link's href value.

• All of the above in this section holds for Firefox, Safari, Camino, and Chrome, but not quite for Opera, which seems to implement the standard Range object differently than do the former browsers. Without getting into the details, I find when using Opera that I can access the anchor element node with range.startContainer.parentNode but not via the commonAncestorContainer and endContainer Range properties.

No Range required

It's actually not necessary to go through a Range object to get our hands on the My favorite Web site is worth clicking on... text node; Mozilla's Selection interface has an anchorNode property, which [r]eturns the node in which the selection begins, and a focusNode property, which [r]eturns the node in which the selection ends, that will allow us to do that, e.g.:

if (selObj.anchorNode.parentNode.tagName == "A") { ...color the link text or take some other action... }
else window.alert("Sorry, we are unable to determine whether link creation was successful or not.");


Text activation

We noted in the previous post that with MSIE for Windows it is not necessary to 'activate' the second paragraph, via either a document.designMode="on" or contentEditable="true" assignment, to make execCommand( ) changes to it - a good thing in this case as it minimizes the editability of the demo document. Once the code has been cross-browserized, text activation is similarly not required for Opera but is required for Firefox, Safari, Camino, and Chrome to run the demo.

For Safari and Chrome, editability can be effectively minimized by enabling and disabling contentEditable for the second paragraph on the fly prior to the createLink command and after the foreColor command, respectively:
...
document.getElementById("p1").contentEditable = "true";
document.execCommand("createLink", false, linkURL);
if (selObj.anchorNode.parentNode.tagName == "A") {
	document.execCommand("foreColor", false, "#ff0033");
	document.getElementById("p1").contentEditable = "false"; }
...
<p id="p1" style="color:#3366cc;">My favorite Web site is worth clicking on...</p>
For Firefox and Camino, editability can be suppressed post-execution but not pre-execution. Curiously, turning contentEditable on for the second paragraph per the above code "collapses" the selection* to a single point when using Firefox and Camino (selObj.isCollapsed returns true) - even more strangely, the collapse point is sent to the beginning of the button element's child text node (selObj.anchorNode.wholeText returns Click to add link, selObj.anchorOffset returns 0) - contentEditable should be set before the AddLink( ) function is called, and therefore you'll be starting with an editable second paragraph, with these browsers.

(*Although the selection is collapsed, the second paragraph is still activated, and a second run through the AddLink( ) function will get the demo to work, but a demo that doesn't work on a first try isn't worth anyone's while, needless to say. Moreover, it's not strictly true that editability cannot be suppressed pre-execution for Firefox and Camino as it is possible to re-add the original selection range to the collapsed selection prior to the createLink command, but this is a can of worms that I would just as soon not open.)

But it is at least possible to statically set document.getElementById("p1").contentEditable to true selectively for Firefox and Camino, and thereby for non-Mozilla browsers avoid giving the p1 p element a contentEditable="true" attribute, as follows:

...
<button type="button" onclick="AddLink( );">Click to add link</button>
<script type="text/javascript">
if (window.globalStorage && window.postMessage) /* Flags Firefox 3+ - see this JavaScript Kit page. */
document.getElementById("p1").contentEditable = "true";
</script>


FYI: Firefox and Camino will not color the link text, give a pointer cursor over the link, or write the link URL to the browser window's status bar unless editability is turned off after the foreColor command.

Creating more than one link

Opera, Safari, and Chrome allow me to create more than one link in the second paragraph without incident.

Upon pre-activating the second paragraph, I can create a first link with Firefox and Camino straightforwardly, but creating two or more links with these browsers is problematic - here's a chronology of what happens:
(1) After a first link is created and colored, p1's editability is turned off.
(2) Upon trying to create a second link in p1, the user selection is collapsed as described above; however, our second run through AddLink( ) turns p1's editability back on.
(3) A second attempt to create a second link in p1 is successful; however, in this AddLink( ) run selObj.anchorNode.parentNode returns [object HTMLParagraphElement], i.e., the anchor element markup is not detected. As a result, the foreColor command is not executed for the second link, and p1's editability remains on.
(4) With p1 editable, the browser does not impart normal click and mouseover behavior to the second link and it subtracts this behavior from the first link; it also sometimes (not reproducibly) subtracts the #ff0033 color from the first link.
(Steps (3) and (4) repeat with attempts to create additional links.)

So we now have two 'crippled' links: they're underlined and they have the browser's default link color (#0000ee), but clicking them doesn't take us to their target documents, and mousing over them neither loads their href values into the status bar nor gives us a pointer cursor. However, control-clicking these links does pop up a context menu appropriate for a link
[Firefox's link context menu]
and whose Open Link in New Window and Open Link in New Tab commands work as advertised.

Demo

The div below holds a cross-browser version of the "Creating a Link" demo combining Microsoft's original code with some of the code given in this post. Regarding browser support, the demo should work with the most recent versions of MSIE, Firefox, Opera, and Safari, and also with Camino and Chrome. For MSIE and Opera, the foreColor command will not take effect until the new link is deselected. For Firefox and Camino, the demo is good for creating one link, but I make no promises beyond that.

<title>Microsoft's "Creating a Link" Demo, Cross-Browser Version</title>

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!




In the following entry, we'll examine in greater detail the "custom dialog business" that I gave short shrift to in the "Your URL, comrade" section.

reptile7

Tuesday, June 08, 2010
 
CreateLinking for the Microsoft Masses
Blog Entry #181

In the previous entry we discussed the copy execCommand( ) command as part of our efforts to adapt HTML Goodies' "Click... It's Copied!" script to non-MSIE browsers. Copy is not a typical execCommand( ) command in that it doesn't change a document in any way, so I thought it would be a good idea to check over an execCommand( )-based application that does make document changes, and the MSDN Library's execCommand( ) method page helpfully provides an Example that does just that and that we'll deconstruct in today's post.

Specifically, Microsoft's execCommand( ) example converts selected text to a link with the createLink execCommand( ) command and then colors the link text with the foreColor execCommand( ) command. The example's script contains various proprietary features that prevent the browsers on my iMac from running it; fortunately, these features have standard or on-track-to-be-standard equivalents via which the script can be cross-browserized.

If you're an MSIE 4+ user, you'll see below the example a button* that when clicked opens a new window/document holding a demo for the example, which, from a session at my local library, I can confirm works very nicely with MSIE 8, although I can't vouch for other Windows browsers.
(*Non-MSIE 4+ users will see a prompt for installing the latest version of MSIE. Sorry, that prompt isn't there anymore although you can see it at this archived page.)

The unselectables

Let's begin, then, with a look at the example script's document body HTML, which codes two paragraphs and a push button:

<p unselectable="on">Select any portion of the following blue text, such as &quot;My favorite Web site&quot;. Click the button to turn the selected text into a link.</p>
<p style="color= #3366CC">My favorite Web site is worth clicking on. Don't forget to check out my favorite music group!</p>
<button onclick="AddLink( );" unselectable="on">Click to add link</button>


The first paragraph instructs the user to select some text in the second paragraph and then click the button, which will trigger an AddLink( ) function that acts on the user's selection as described in the next section.

The second paragraph is given an inline style that imparts to its text a light-blue color; the style syntax as given in the example (color= #3366CC) is incorrect but is specified correctly (color:#3366CC) in the demo source.

The first paragraph and the push button and also the execCommand Example: Creating a Link h1 heading on the demo page are all given an unselectable="on" attribute. Unselectable is a Microsoft DHTML property that [s]pecifies that an element cannot be selected when set to on, and is applied by Microsoft to most document body elements. Unusually for a DHTML property, unselectable is implemented only as an HTML attribute and not as a script property; consequently, an
if (document.elementObject.unselectable) window.alert("Yes"); else window.alert("No");-type
conditional cannot be used to assess unselectable's browser support.

On my computer, the unselectable="on" attributes for the p and h1 elements (but not for the button element) are supported by Opera, and that's it; alternatively, replacing these attributes with onmousedown="return false;" event handlers would prevent p/h1 text selection in a cross-browser manner.

(FWIW, I am able to select button element text with MSIE, Opera, Safari, and Chrome, but not with Firefox and Camino. Why a user would want to select button text in the first place is not clear to me, however.)

I was going to comment on the unnecessary use of &quot; references to quote the My favorite Web site substring in the first paragraph, but the W3C notes, Some authors use the character entity reference "&quot;" to encode instances of the double quote mark (") since that character may be used to delimit attribute values, so fair enough.

Link and color

So, let's say we select with the mouse cursor My favorite Web site at the beginning of the second paragraph and click the button. Our analysis now moves to the script's script element and its AddLink( ) function, which kicks off with a bang of a statement:
<script type="text/javascript">
function AddLink( ) {
	// Identify selected text
	var sText = document.selection.createRange( );
Back in 1997 or so, with the advent of their 'level 4' browsers, Netscape and Microsoft implemented markedly different ways to get at a mousical text selection. Netscape gave the document object a (now-deprecated) getSelection( ) method that [r]eturns a string containing the text of the current selection. In contrast, Microsoft's approach to selection access was much more sophisticated, involving the introduction of a selection object that simultaneously (a) was a document object property** and (b) itself had one property and a small set of methods, chief among them a createRange( ) method that creates a TextRange object representing the text of the selection.

(**Mind you, this is all on the Windows side. MSIE 5.x for the Mac returns not [object Selection] but null for document.selection - this seems to be another one of those 'support without implementation' situations.)

Got all that? The first AddLink( ) statement therefore gives us a new TextRange object with an sText identifier for the selected My favorite Web site string. This TextRange object is immediately put to use in the condition of the subsequent if...else statement comprising the remainder of the function:

if (sText.text != "") { ... }
else { window.alert("Please select some text!"); } }
</script>


The TextRange object has a text property that [s]ets or retrieves the text contained within the range. Had we not made a selection, sText.text would evaluate to an empty string, the if condition would return false, and the else clause's Please select some text! alert( ) message would pop up.

But we did in fact make a selection - sText.text returns My favorite Web site - so the if clause is operative.
if (sText.text != "") {
	// Create link
	document.execCommand("createLink");
	// Change the color to indicate success
	if (sText.parentElement( ).tagName == "A") {
		sText.execCommand("foreColor", false, "#ff0033"); } }
Let's make a link! After an initial comment, the if block begins with a document.execCommand("createLink"); command that, per its syntax - the second execCommand( ) parameter is omitted and thus defaults to true - and at least when using MSIE 4+ for Windows, pops up a dialog box that asks for a link URL and looks like this (the image below shows a likeness of the box but is not a bona fide screen shot):

[Microsoft's createLink dialog box]

The box contains a selection list for choosing a URL scheme (http, https, ftp, etc.) and a text field for filling in the rest of the URL. Let's say we append www.garageband.com (a site certainly suitable for "check[ing] out my favorite music group") to the http:// in the text field and then click the box's button.

Control now passes to a concluding if statement that first checks if My favorite Web site has been marked up with an anchor element:

if (sText.parentElement( ).tagName == "A") { ... }

The condition code is reasonably intuitive:
parentElement( ), a method of the TextRange object, [r]etrieves the parent element for the given text range.
• The read-only tagName property [r]etrieves the tag name of the object - a link object in this case. tagName was implemented by Microsoft in MSIE 4 but was picked up by the W3C shortly thereafter for the DOM Level 1 Core, and all modern browsers support it. As part of the DOM's Element interface, tagName applies to all elements. In an HTML document, tagName gives an all-uppercase return string: "a" would cause the condition to return false.

If the if condition is true, i.e., if link creation was successful, then the link text is given a "strawvery red" color via an sText.execCommand("foreColor", false, "#ff0033"); command. The color does not take effect until My favorite Web site (it's underlined at this point, if I recall correctly) is deselected.

In actual practice, our new My favorite Web site link exhibits the behavior of a link: upon mousing over it from the text to its right, the mouse cursor changes from an I-beam (cursor:text;) to a little hand (cursor:pointer;) and http://www.garageband.com is written to the browser window's status bar. And of course, clicking it will take you to http://www.garageband.com in the same window.
(FYI: GarageBand.com packed it in shortly after this post was written.)

Pretty straightforward, huh? Three last points:

(1) Significantly, the example script does not set the designMode property of the document to on, nor does it set contentEditable to true for the second paragraph.

(2) sText could have been the calling object for the createLink command, whereas the document object could have been the calling object for the foreColor command.

(3) Of the browsers on my computer, Opera alone supports Microsoft's selection object and the selection object's createRange( ) method. However, Opera won't run the demo because it doesn't support the user-interface form of the createLink command (it also doesn't support the parentElement( ) and execCommand( ) methods of the TextRange object).

Now then: How do we adapt this guy to other browsers? That's a task for our next episode.

reptile7


Powered by Blogger

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