Working with HTML Using the FrontPage 2003 Object Model

 

Lisa Wollin
Microsoft Corporation

September 2004

Applies to:
    Microsoft Office FrontPage 2003

Summary:   Learn how to use the Microsoft Office FrontPage 2003 Visual Basic for Applications object model to programmatically add or modify the HTML in a Web page. (11 printed pages)

Contents

Introduction
Understanding the FrontPage Object Model
Troubleshooting
Conclusion

Introduction

Whether you create complex add-ins to sell to users of Microsoft Office FrontPage 2003 or write simple macros to automate your own tasks, you likely have needed to programmatically access the HTML in your Web pages. This article provides detailed information about using the FrontPage Visual Basic for Applications object model to insert, change, and remove HTML in your Web pages. To help you get started, you need a general understanding of the FrontPage object model. The following section helps you to navigate the FrontPage object model. In addition, you may find the following resources helpful as you create macros and add-ins that extend the FrontPage application.

Understanding the FrontPage Object Model

The FrontPage Object Model is really comprised of two separate object models: the Web object model and the Page object model. The Web object model provides objects and collections to access part of a Web site and the FrontPage application. The Page object model provides access to the elements within the HTML pages. When you create macros or add-ins that modify the HTML in the page, you use the Page object model, but if you have ever tried to navigate the Page object model, you may have found it difficult to understand. This article helps you understand the Page object model and determine which objects to use to access elements in an HTML page.

Note   The code samples in this article are written in Visual Basic for Applications (VBA). Therefore, you can run any of the code in this article in the Visual Basic Editor included with FrontPage. However, you can access the FrontPage Object Model from Microsoft Visual Basic, Visual Basic .NET, Microsoft Visual C, Microsoft Visual C++, or any language that allows you to write COM or managed code.

The FrontPage Page object model exposes two kinds of objects that access elements in an HTML page: FPHTML and IHTML objects. For most elements in a Web page, there are corresponding IHTML objects, and for most IHTML objects, there are corresponding FPHTML objects. The main difference between IHTML and FPHTML objects is that IHTML objects are the original objects from the Internet Explorer API and contain all the original properties, methods, and events from the Internet Explorer API. The Internet Explorer API was designed for working with the page in a run-time environment, and code that you write for FrontPage works with a page in a design-time environment. Therefore, the FPHTML objects are expanded to include properties, methods, and events that are appropriate in a design-time environment. For more information, see Understanding the differences between the FPHTML objects and the IHTML objects.

Accessing the Document

Before you can manipulate the HTML in a Web page, you must access the page. To do this, you use the ActiveDocument property. This assumes that the page currently open in FrontPage is the one with which you want to work.

Note   You cannot programmatically modify the HTML in a Web page in FrontPage without opening the document. However, you can open a document in a window that is hidden. You can do this by setting the ViewMode property of the PageWindowEx object to fpPageViewNoWindow. You must use the Open method to open the file into the active window, and then use the ActivePageWindow property to access the window and the ActiveDocument property to access the page.

The ActiveDocument property returns an FPHTMLDocument object. After you have accessed the document, you can manipulate the HTML in the page. The following sections explain how to access elements in a Web page and insert text and HTML.

Accessing Collections of Elements

You can access collections of elements in a variety of ways. The all property of the FPHTMLDocument object returns a collection of all elements in a page. This includes elements in the HEAD section of the page. The all property is a member of the FPHTMLDocument object as well as most other objects and returns an IHTMLElementCollection object that represents a collection of all elements nested within the parent element, but not the parent element itself. For example, the following code returns all elements nested within the HTML element.

ActiveDocument.all

If you want to return all elements nested within the BODY section of the page, you could use the following code.

ActiveDocument.body.all

The all property applies to all elements in a Web page regardless of whether the element has nested elements. If an element does not have nested elements, the all property returns an empty collection.

Note   An empty collection is not a null collection. An empty collection has a value of 0 for the length property, which indicates that no objects are within the collection but the collection object itself is not null. Therefore, if you use the all property to check whether any nested elements are within a parent element, use the length property of the IHTMLElementCollection object to determine whether any objects are in the collection.

You can use the tags method of the IHTMLElementCollection object to return collections of specific elements. For example, the following code returns an IHTMLElementCollection object that represents all P elements in a page.

ActiveDocument.all.tags("p")

Some collections can be accessed directly through accessor properties. For example, you can use the images property of the FPHTMLDocument object to return an IHTMLElementCollection object that represents all the IMG elements contained within a page. This is also true for forms (forms property), hyperlinks (links property), embedded objects (embeds property), bookmarks (anchors property), and a few other elements. These properties all return IHTMLElementCollection objects that represent collections of a specific element.

Accessing Individual Elements

You can access the BODY element by using the body property of the FPHTMLDocument object. However, there is no way to access any other elements directly, so to access individual elements you need to access the appropriate collection and then use the item method to access a specific element within the collection. The item method takes either a number or a string, so you can locate individual elements by using the index number of the element (the element's position within the collection) or the value of the element's id attribute. The following code returns an IHTMLElement object for the HEAD element in the page.

ActiveDocument.all.tags("head").Item(0)

Important   All collections in the FrontPage Page object model are zero-based, so the first item in a collection always has an index of 0. Because a Web page has only one HEAD element, the HEAD element always has an index of 0.

If you use the element's id attribute to return a specific element, you need to know the id attribute for the element. The following code returns an IHTMLElement object for a P element that contains an id attribute value of "first", such as <p id="first">...</p>.

ActiveDocument.all.tags("p").Item("first")

Note   If your page has more than one element of the same type that have the same id attribute value, the preceding code example returns an IHTMLElementCollection object rather than an IHTMLElement object.

When you have access to a specific element, you can set the attributes for that property. The item method returns an IHTMLElement object; however, all element objects have a base of the IHTMLElement object, so you can assign any element to its corresponding FPHTML or IHTML object.

For example, if you want to access a P element, you can use either the FPHTMLParaElement object or the IHTMLParaElement object. The following example assigns the P element with the id attribute value of "first" to an FPHTMLParaElement object.

Dim objP As FPHTMLParaElement
Set objP = ActiveDocument.all.tags("p").Item("first")

Note   If you use an object that is inappropriate for an element—for example, if you assign the BODY element to an FPHTMLParaElement object—FrontPage raises a "Type mismatch" error. For more information on mitigating type mismatch errors, see Type Mismatch in the "Troubleshooting" section later in this article.

When you have access to the element, you can set attributes, change text, even insert HTML. For most, if not all, objects, you can find properties that correspond to attributes. For example, for an FPHTMLImg object, a src property corresponds to the src attribute and an alt property corresponds to the alt attribute. Many objects also have an id property that corresponds to the value of the id attribute for the element.

Accessing the Position of the Insertion Point

Accessing specific elements is relatively simple. However, usually you want to access the element that is at the position of the insertion point. You may want to access the text and HTML elements that are selected or perhaps just insert text or HTML into the page without replacing any existing text. There are two ways to do this. One way uses the activeElement property, and the other uses an IHTMLSelectionObject object.

Accessing the active element

Sometimes you might want to access the currently selected element or the element that is at the position of the insertion point. To do this, you use the activeElement property of the FPHTMLDocument or IHTMLDocument object. The activeElement property returns an IHTMLElement object. The following example uses the activeElement property to create an object variable that accesses the element at the insertion point.

Dim objElement As IHTMLElement
Set objElement = ActiveDocument.activeElement

The previous example uses an IHTMLElement object to store a pointer to the active element. However, sometimes you might want to use the exact type of object. In this case you need to know the type of the active element. To do this, you can use the tagName property to return a String that contains the name of the tag. Then you need to determine which FPHTML object corresponds to that element. The following example shows a Select statement that sets the object variable based on the value of the tagName property.

Dim strTagName As String
strTagName = ActiveDocument.activeElement.TagName
Select Case LCase(strTagName)
    Case "body"
        Dim objBody As FPHTMLBody
        Set objBody = ActiveDocument.activeElement
    Case "p"
        Dim objP As FPHTMLParaElement
        Set objP = ActiveDocument.activeElement
    Case "div"
        Dim objDiv As FPHTMLDivElement
        Set objDiv = ActiveDocument.activeElement
    Case "span"
        Dim objSpan As FPHTMLSpanElement
        Set objSpan = ActiveDocument.activeElement
    Case Else
        Dim objElement As IHTMLElement
        Set objElement = ActiveDocument.activeElement
End Select

Accessing selected text and HTML

To access the selected text or the position of the insertion point, use the createRange method of the IHTMLSelectionObject object to create an IHTMLTxtRange object. To access the IHTMLSelectionObject object for a document, use the selection property of the FPHTMLDocument object.

The following example creates an IHTMLTxtRange object with the currently selected text. If no text is selected, the example accesses the position of the insertion point.

Dim objSelection As IHTMLTxtRange
Set objSelection = ActiveDocument.Selection.createRange

You can also create an IHTMLTxtRange object by using the createTextRange method of the IHTMLBodyElement or FPHTMLBody object. This approach places the entire body of the page into the text range.

Note   You can access the createTextRange method from the FPHTMLButtonElement, IHTMLButtonElement, FPHTMLInputButtonElement, IHTMLInputButtonElement, FPHTMLInputHiddenElement, IHTMLInputHiddenElement, FPHTMLInputTextElement, IHTMLInputTextElement, FPHTMLTextAreaElement, and IHTMLTextAreaElement objects; however, doing so doesn't have an effective use in this case, so this article doesn't discuss the createTextRange method in relation to these objects.

Inserting text at the insertion point

After you create a text range with the selection, you can insert text or HTML at the location of the insertion point and replace the current selection with text or HTML.

You use the text property to insert text at the insertion point, as shown in the following example.

Dim objSelection As IHTMLTxtRange
Set objSelection = ActiveDocument.Selection.createRange
objSelection.Text = "The quick red fox jumped over the lazy brown dog."

Inserting HTML at the insertion point

If you use the text property to insert HTML, FrontPage converts the tags and special symbols to their HTML equivalents. Therefore, to insert HTML into a page, use the pasteHTML method of the IHTMLTxtRange object, as shown in the following example.

Dim objSelection As IHTMLTxtRange
Set objSelection = ActiveDocument.Selection.createRange
objSelection.pasteHTML "<b>The quick red fox jumped over the lazy brown dog.</b>"

The IHTMLTxtRange object includes an htmlText property that you can use to read the selected HTML and text. However, the htmlText property is read-only; therefore, you can't use it to replace the selected HTML. In this case, use the htmlText property to read the selected text and HTML and the pasteHTML method to replace the selected text and HTML with HTML code. The following example shows how you might do this.

Dim objSelection As IHTMLTxtRange
Set objSelection = ActiveDocument.Selection.createRange
objSelection.pasteHTML "<div>" & objSelection.htmlText & "</div>"

Note   The preceding code adds a <div> tag around the currently selected text. You should know and understand the HTML specification and HTML block elements to ensure the VBA code actually generates valid HTML. For example, adding only a table cell without the corresponding table row and table tags in the middle of a paragraph generates invalid HTML.

Collapsing a text range

Sometimes a user might have selected text, but the code that runs assumes that the user has no text selected. If the code that you need to write requires that the user has no text selected (for example, if you are inserting text or code that replaces selected text or code), you can use the collapse method to collapse the range. The collapse method has an optional Boolean parameter named start that indicates whether the range collapses to the start of the range or the end of the range. The default value is True, which indicates that the range collapses at the beginning of the range; a value of False indicates that the range collapses at the end of the range. The following example uses the collapse method to collapse the range at the end of the selected range and then inserts HTML code.

Dim objSelection As IHTMLTxtRange
Set objSelection = ActiveDocument.Selection.createRange
objSelection.collapse False
objSelection.pasteHTML "<div>The quick red fox jumped over the lazy brown dog.</div>"

Inserting Text and HTML into a Page

In addition to using the IHTMLTxtRange object to insert text and HTML code, you can also use the insertAdjacentText and insertAdjacentHTML methods. The insertAdjacentText and insertAdjacentHTML methods are members of the IHTMLElement object and therefore of most IHTML and FPHTML objects. When you have a handle to the appropriate object, you can use the insertAdjacentText or insertAdjacentHTML method to insert text or HTML into the document.

Both the insertAdjacentText and insertAdjacentHTML methods have a where parameter that takes a string and indicates whether the text or HTML is inserted before or after the opening or closing tag for the element. The possible values are "beforeBegin", "afterBegin", "beforeEnd", and "afterEnd". The following example inserts HTML before the closing tag if the active element is the BODY element and pastes it after the closing tag for all other elements.

Dim objElement As IHTMLElement
Dim strTagName As String
Set objElement = ActiveDocument.activeElement
strTagName = objElement.TagName
Select Case strTagName
    Case "body"
        objElement.insertAdjacentHTML where:="beforeEnd", _
            HTML:="<div>The quick red fox jumped over the lazy brown dog.</div>"
    Case Else
        objElement.insertAdjacentHTML where:="afterEnd", _
            HTML:="<div>The quick red fox jumped over the lazy brown dog.</div>"
End Select

Programmatically Selecting Text and Code

At times you may want to programmatically select text and code. You can do this using the select method of the IHTMLTxtRange object. Regardless of whether any text or code is selected, you can specify the element to select by using the moveToElementText method of the IHTMLTxtRange object, or you can select a specified range of text and code by using the move, moveEnd, or moveStart methods of the IHTMLTxtRange object.

The following example uses the moveToElementText and select methods to select the active element and the text it contains. If the selection spans several elements, the active element is the element that contains all other selected elements.

Dim objRange As IHTMLTxtRange
Dim objElement As IHTMLElement

Set objRange = ActiveDocument.Selection.createRange
Set objElement = ActiveDocument.activeElement

objRange.moveToElementText objElement
objRange.Select

Inserting META Data, Styles, and Scripts

As mentioned previously, the HEAD element of a Web page doesn't have an accessor property that you can use to access it and all child elements. There is also no corresponding FPHTML or IHTML object that you can use to access elements that are typically in the HEAD section of a Web page. To access some child elements for the HEAD element, such as the TITLE element, you can use the corresponding property of the FPHTMLDocument object, such as the title property.

Sometimes you might need to insert content into the HEAD section of a Web page, such as scripts, META data, and styles; for this reason you may need to access the HEAD element. To do this, you can use the method shown previously and create an IHTMLElement object variable, as shown in the following example.

Dim objHead As IHTMLElement 
Set objHead = ActiveDocument.all.tags("head").Item(0)

When you have access to the HEAD element, you can use the all method (as described previously) to access the child META, STYLE, and SCRIPT elements or use the InsertAdjacentHTML method to insert new elements into the HEAD section of a Web page. For example, you might need to add META data or insert a SCRIPT element. For more information and code examples, see Accessing Scripts and Adding Styles to Specified Elements.

Troubleshooting

Sometimes code you have written raises errors that you need to troubleshoot. This section lists the most common errors that FrontPage raises when you attempt to modify the HTML programmatically and provides tips to resolve the errors.

The best way to mitigate errors that FrontPage raises when running code is to add error handling inside your code. If you are aware of the likely errors and when they may occur, you can build the appropriate error handling into your code to handle situations in which errors may arise.

Permission Denied

One of the most common errors when working with HTML code programmatically is the "Permission denied" error. In previous versions of FrontPage, you couldn't programmatically modify the HTML while the page was displayed in HTML view; you received a "Permission denied" error. In FrontPage 2003, you can programmatically modify the HTML while in Code view; however, in some cases, FrontPage still raises a "Permission denied" error.

FrontPage raises a "Permission denied" error when the HTML in a page needs to be reparsed. Reparsing the code allows FrontPage to insert HTML in the appropriate location. Reparsing is necessary when you have made significant modifications to the HTML in a page since the last time you saved it or if you have started a new page and haven't yet saved it.

You can reparse the HTML code in two ways: switch to Design view or save the page. Programmatically, you can use the parseCodeChanges method. (In some situations, the parseCodeChanges method doesn't properly reparse the HTML, so you should add appropriate error handling.) For example, the following macro inserts a P element around selected HTML.

Private Sub InsertPElement()
    Dim objRange As IHTMLTxtRange
    Dim strText As String

    On Error GoTo ErrorHandler

    ActiveDocument.parseCodeChanges
    Set objRange = ActiveDocument.Selection.createRange
    strText = objRange.htmlText
    strText = "<P>" & strText & "</P>"

    objRange.pasteHTML strText
    ActiveDocument.parseCodeChanges

ExitSub:
    Exit Sub

ErrorHandler:
    MsgBox "You need to save your document before you can insert the tag."
    GoTo ExitSub

End Sub

Notice that the preceding code calls the parseCodeChanges method both before and after inserting the HTML. However, if too many changes have been made and the parseCodeChanges method doesn't function as expected to prepare the page to receive additional code changes programmatically, there is additional error handling. In this case, the error handler does nothing more than display a message telling the user to save the page. Potentially, you could provide code that saves the document before attempting to insert the code.

To determine whether a page has been changed since it was last saved, you could use the isDirty property of the FPHTMLDocument object. If the isDirty property returns False, the page is ready to receive HTML programmatically; if the isDirty property returns True, you can save the page before inserting the HTML. The following code saves the current page if it has changed since it was last saved.

If ActiveDocument.IsDirty = True Then
    ActivePageWindow.Save
End If

This code assumes that the page was previously saved. If it wasn't previously saved, the Save method fails and you receive a run-time error. To programmatically save a page, use the FileDialog object. The FileDialog object is an Office shared object. A full discussion of the FileDialog object and related objects is beyond the scope of this article. For more information, see Using the FileDialog Objects.

The other way to reparse code changes in FrontPage is to switch view mode, as mentioned previously. In earlier versions of FrontPage, switching the view mode while you had code selected in HTML view collapsed the selection and moved the insertion point. However, in FrontPage 2003, you can easily switch the view mode programmatically without losing the selection in Code view.

Note   The only exception is if the text that you have selected in Code view is located outside the BODY section of the page, such as in the HEAD section. In this case, switching to Design view moves the insertion point to the beginning of the BODY section, causing you to lose the selection.

The following example shows how to switch the view mode programmatically to reparse the code changes in a Web page, and then to switch back and create an IHTMLSelectionObject object from the selected code.

Dim fpView As FpPageViewMode
Dim objRange As IHTMLTxtRange

If ActiveDocument.IsDirty = True Then
    fpView = ActivePageWindow.ViewMode
    ActivePageWindow.ViewMode = fpPageViewNormal
    ActivePageWindow.ViewMode = fpView
End If

Set objRange = ActiveDocument.Selection.createRange

Type Mismatch

The second most common error when working with FrontPage is the "Type mismatch" error. Mentioned earlier, the "Type mismatch" error occurs when you attempt to assign an element to the wrong type of object—for example, assigning the BODY element to an FPHTMLParaElement object.

Sometimes you might need to determine programmatically what type of object to use. Because all of the methods return IHTMLElement objects or IHTMLElementCollection objects, you may not be able to easily determine this programmatically.

To eliminate "Type mismatch" errors, you can use the IHTMLElement object to work with an element programmatically because every element object has the IHTMLElement object as its base object. However, when you are writing code, you won't have access to the properties and methods that are unique to the FPHTML objects.

In this case, you must refer to the FrontPage VBA online help to determine which objects to use with which elements. For example, on first glance, the FPHTMLHeaderElement and IHTMLHeaderElement objects appear to be objects for the HEAD element. However, if you look at the online help for these objects, you see that they actually are objects for the H1 through H9 elements. When you know this fact, you can eliminate "Type mismatch" errors at run time by assigning elements to the correct objects.

Conclusion

When you understand how the FrontPage Page object model functions and how to navigate through the objects, writing code that modifies the HTML in a page can be fun. And when you think of all the things that users can do with FrontPage, you certainly will find new reasons for programmatically modifying the HTML code in your Web pages.

For more assistance with working with the FrontPage object model, see the FrontPage 2003 VBA Language References (Page Object Model and Web Object Model) and the FrontPage 2002 Software Developer Kit, which you can view online or download and install on your local computer. You may also want to check out the white paper HTML Code Best Practices.