reptile7's JavaScript blog
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!"); } }

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
	// 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 (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 is written to the browser window's status bar. And of course, clicking it will take you to in the same window.
(FYI: 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.


Comments: Post a Comment

<< Home

Powered by Blogger

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