Using the TextRange Object to Manipulate Text

In the June, 1998 issue of Microsoft WebBuilder, the article "Creating dynamic tables with innerText" discussed using the innerText/innerHTML and outerText/outerHTML properties to manipulate text in your HTML documents. This month, we'll introduce you to some more advanced text manipulation techniques using Microsoft's Dynamic HTML TextRange object. Using the TextRange object, you can search for text in your document, move through your text to a given element or point, and select and change both the document's text and HTML tags.

Creating TextRange objects

A TextRange object doesn't actually hold any text itself, but references text by determining a start and end position over the text stream in a document. The text between the start and end points is referred to as the text range.

A document's text stream includes all of your HTML code, but with all tag definitions stripped off. Consider the example code below:


<HTML>
<HEAD>
<TITLE> TextRange example
</TITLE>
</HEAD>
<BODY>
<H1> The TextRange Object </H1>
<P> Use the TextRange object for:<BR>
<UL>
  <LI> Searching for text </LI>
  <LI> Selecting text </LI>
  <LI> Changing text </LI>
</UL>
</BODY>

Creating a TextRange object over the BODY of this HTML document would position the starting point of the text range at the beginning of any text found after the <BODY> tag and position the end point at the end of any text before the </BODY> tag. The TextRange object would then refer to the following text stream:


The TextRange Object Use the TextRange object for: 
	Searching for text Selecting text Changing text 

In order to use a TextRange object, you have to create one, making sure that the starting and ending points encompass the text you want to search or change. To create your object, you can use the TextRange object's createTextRange method, as in the following example:


var someText = document.body.createTextRange();

The variable sometext now contains a reference to all of the text in the body of the document. This method can be used to create TextRange objects from the text within a BODY, BUTTON, TEXTAREA, or an INPUT element whose TYPE = "text."

You can also create a text range from a selection made by the user by using the createRange method on the selection object, as in:


var someText = document.selection.createRange();
Finding and changing text

One handy use of the TextRange object is to find text within your document and then change either the text itself or its appearance. Figure A shows the initial state of a page, which includes a list of topic areas, some text in tables, a text box, and a button.

Figure A: Initially, our page looks like this.
mit98c2a.gif (14195 bytes)

When the user enters text in the text box, and then clicks the button, all occurrences of the word he entered become bold, and the button text also changes. Figure B shows the page after the word unit has been entered and the button has been clicked.

Figure B: All occurrences of the text enter by the user are bolded using the findText method.
mit98c2b.gif (14641 bytes)

To create this page, start by creating a textbox in our HTML BODY, named tochange, in which the user can enter his selection, and a button that will capture the onclick event:


<INPUT name = "tochange"> 
<INPUT type = "button" value = 
  "Find text"onclick = "ChangeText()">

Next, we write a function, ChangeText, that will find and change the text for us. The ChangeText function, as shown in Listing A, uses the TextRange findText method to find all occurrences of the text that was entered. The findText method moves the text range reference to the first occurrence of the text it has been told to find, and returns true if the text was found and false if it wasn't.

Listing A: Function to find and change text


function ChangeText()  {
  var newText = document.body.createTextRange();
  if (tochange.value != "")
      while (newText.findText(tochange.value) )
          newText.pasteHTML("<FONT style =
          'font-weight:bold'>" + tochange.value  
          "</FONT>");
  var btntext = document.all.mybutton.createTextRange();
  btntext.text = "You clicked me!"
}

Looking at the code, you'll see that the ChangeText function starts off by creating a TextRange object, newText, over the body of the document. If there's indeed text in the textbox, the while loop searches the text referenced by newText for occurrences of the value in the text box. The function then uses the TextRange pasteHTML method to replace each occurrence of the text it finds with the same text surrounded by new HTML tags that bold the text.

Text and HTML elements passed to the pasteHTML method completely replace any text or elements previously in the text range. You could also use this method to replace the text itself, rather than just altering its appearance, as we've done.

The last part of the ChangeText function illustrates how you can change the text on a button to give the user feedback that the button has been clicked. The code


var btntext =  
  document.all.mybutton.createTextRange();
btntext.text = "You clicked me!"

creates a TextRange object over the text of the button mybutton. The TextRange text property is a read/write property that allows you to both retrieve and change the text within the text range. Unlike the pasteHTML method, the text property translates everything you assign it as text and doesn't interpret HTML tags.

Making text move

Our second JScript function, SelectText, illustrates some of the other TextRange methods available to us, particularly the various move methods, of which there are several. We use them here to copy and move sections of our page from one place on the page to another. This should save you time if you want information to appear in more than one place on a page, but don't want to type it in more than once.

If you look at our initial page in Figure A, you'll see the list of topic areas from which the user can choose. When the user clicks on a topic area, a sentence appears telling the user which function matches the topic area he has chosen. The sentence includes text from the topic area, as well as the function name. Figure C shows the state of the page after the user clicked on the first topic.

Figure C:
mit98c2c.gif (15517 bytes)

The text for the sentence that appears when the user chooses a topic isn't actually written anywhere in the code itself. Instead, fragments of the sentence are copied from their original places on the page. We'll use the TextRange properties and methods to copy the topic area from one place on the page, and the function name from another, and then put them together to form our new sentence.

To accomplish this, each topic area must trap the onclick event, and pass a numerical value to the SelectText function so it knows which topic was chosen. The BODY code for placing the topic areas on the page follows:


<LI > <SPAN onclick="SelectText(1)"> search  
  for text in a document? 
  </SPAN> </LI>
<LI> <SPAN onclick="SelectText(2)"> expand 
  a range to include a whole sentence?  
  </SPAN> </LI>
<LI> <SPAN onclick="SelectText(3)"> make a 
  duplicate of a text range? 
  </SPAN> </LI>
<LI> <SPAN onclick="SelectText(4)"> change
  the start and end points of a text range? 
  </SPAN> </LI> 

We then need to write the SelectText function, shown in Listing B. The SelectText function begins by creating a TextRange object over the document body, and then creating a duplicate of the object with the TextRange duplicate method.

Listing B: Function to copy and move text


function SelectText (num) {

//Create a text range on the document body and make a copy.


  var bodySelect = document.body.createTextRange();
  var wordSelect = bodySelect.duplicate();

//Get the topic sentence.


  wordSelect.moveToPoint(window.event.x, window.event.y);
  wordSelect.expand("sentence");

//Remove the punctuation.
wordSelect.moveEnd("character", -3)

//Get the function name.
var funcSelect = bodySelect.duplicate()
switch (num) {
case 1: funcSelect.moveToElementText(row1);
        break;
case 2: funcSelect.moveToElementText(row2);
        break;
case 3: funcSelect.moveToElementText(row3);
        break;
case 4: funcSelect.moveToElementText(row4);
        break;
 }
// Put the new text onto the page.
myText.innerHTML = "To " + wordSelect.text + " use the " 
  + funcSelect.text + " method.";
}

The moveToPoint method is one of several TextRange move methods. moveToPoint moves the beginning and ending points of the text range to one point. We've used it in the SelectText function to move to the point registered by the onclick event when the user chose a topic area. We then use the TextRange expand method, which expands the text range from the single point to enclose the unit that has been passed to the method. A unit can be a character, a word, or a sentence. In this case, we want the entire sentence, which is a collection of words that ends in a punctuation mark. Since each topic area ends in a question mark, and so is indeed a sentence, the expand method encloses the text for the topic the user has selected, and assigns it to the TextRange object wordselect.

We do, however, have a small problem. The question mark at the end of our topic area is included in our wordSelect text range, but we don't want it, since we want to use the topic as a sentence fragment. How do we get rid of it? We can use another move method, moveEnd, which moves the end point of our text range. moveEnd accepts the type of unit you want to move (again, a character, word, or sentence), and a number representing how many units to move.

To include more text in the text range, pass moveEnd a positive number; to exclude text from the range, use a negative number to move the end point back. We want to exclude the question mark, so we need to move a negative number of units. Theoretically, you'd think it should be -1 characters, but in fact it's -3. Perhaps the text range includes some string delimiters at the end that we need to pass over. Regardless, the statement


wordSelect.moveEnd("character", -3)

does the trick for us. Next, we want to grab each function name and tack it onto the end of the sentence we're creating. We can use (you guessed it!) another move function called moveToElementText. This moves a text range to enclose an element, referenced by the element ID. What we've done is enclosed each of the function names that we want to copy within a SPAN block, as you can see from the code, below, that placed the function names on our page:


<TD width = "75" <SPAN id = "row1" > 
  findText(string) </SPAN> </TD>
.
.
.
<TD width = "75" <SPAN  id = "row2"> 
  expand(unit) </SPAN> </TD>
.
.
. 
<TD width = "75" <SPAN  id = "row3"> 
  duplicate() </SPAN> </TD>
.
.
.
 <TD width = "75"  <SPAN id = "row4"> 
  move([start]) </SPAN> </TD>
.
.
.

The number that was passed to the SelectText function when the user clicked on a topic area is then used to identify which function name we want to tack onto the end of our new sentence. If you look at the code for the SelectText function, you'll see that we use a case statement to select the correct element-- that is, the correct function name.

Now that we've got a reference to the topic, and the function name, we can put them together to form our new sentence. We use the <P> element myText, which is a placeholder for the sentence in the BODY of our page, with the following code:


<P id = myText style = 
  "font-weight:bold;color:red">

We can then use our old friend, innerHTML, to create the new statement:


myText.innerHTML = "To " + wordSelect.text 
  + " use the " + funcSelect.text + " 
  method.";

To help you put all this together, you can find the code for the BODY of this project in Listing C.

Listing C: BODY code



<BODY>
<P style = "font-style:italic"> Click on a topic to
	find the correct function.<BR>
<FONT style="color:red;font-size:14pt;font-weight:bold">
  How do I? </FONT>
<UL style= "font-weight:bold; margin-top:0">
  <LI ><SPAN onclick="SelectText(1)"> search for text in 
    a document? </SPAN> </LI>
  <LI> <SPAN onclick="SelectText(2)"> expand a range to 
    include a whole sentence? </SPAN> </LI>
  <LI> <SPAN onclick="SelectText(3)"> make a duplicate of 
    a text range? </SPAN> </LI>
  <LI> <SPAN onclick="SelectText(4)"> change the start 
    and end points of a text range? </SPAN> </LI>
</UL>
<P id = myText style = "font-weight:bold;color:red">
<P>
<P>
<P>
<TABLE id = "functable" border="1" cellpadding="6" 
	cellspacing="0" width="600" cols = 2>   
  <TR> 
    <TD colspan="2" > <FONT style="font-weight:bold;
      font-size:14pt" >Some useful TextRange methods 
      </FONT> <BR>
     <FONT style = "font-style:italic" > To highlight all
      occurrences of a word in the document, enter the 
      word here: </FONT>
      <INPUT name = "tochange">
      <INPUT type = "button" name = mybutton value = 
         "Find text" onclick = "ChangeText()"> 
    </TD>
  </TR>
  <TR>
    <TD width = "75" <SPAN id = "row1" > findText(string) 
      </SPAN> </TD>
    <TD width = "300" >  Searches for the text in the 
      string given and, if found, creates a text range 
      enclosing the text. Returns true if the string was 
      found, false if not. </TD>
  </TR>
  <TR>
    <TD width = "75" <SPAN  id = "row2"> expand(unit) 
     </SPAN> </TD>
    <TD width = "250" >  Expands the text range to 
      include the unit given. </TD>
  </TR>
  <TR>
    <TD width = "75" <SPAN  id = "row3"> duplicate()
      </SPAN></TD>
    <TD width = "250" > Returns a duplicate of the 
       method's TextRange object. </TD>
  </TR>
  <TR>
    <TD width = "75"  <SPAN id = "row4" > move([start]) 
       </SPAN> </TD>
    <TD width = "250" >  Changes the start and end 
       points of a text range to cover the unit given.
    </TD>
  </TR>
</TABLE>
</BODY>
</HTML>

Try this at home!

We could be done at this point, but it would be nice to highlight the function name where it appears in the table when the user is clicking on the topic related to that function. We'll leave that as an exercise for you to try. Write in and let us know how you did it, and we'll share it in our Q & A column.

Conclusion

In this article, we had some fun playing with the different TextRange methods. They are handy for finding and changing text on the fly, but we also like their ability to copy and move text around on a page. There are many TextRange properties that we didn't explore. For more information, you can see the Microsoft reference pages at http://msdn.microsoft.com/library/officedev/office97/output/f1/d4/s5adb0.htm

 

Copyright © 1998, ZD Inc. All rights reserved. ZD Journals and the ZD Journals logo are trademarks of ZD Inc. Reproduction in whole or in part in any form or medium without express written permission of ZD Inc. is prohibited. All other product names and logos are trademarks or registered trademarks of their respective owners.