How to Make An Animated Menu Using HTML+TIME Transitions

This topic documents a feature of HTML+TIME 2.0, which is obsolete as of Windows Internet Explorer 9.

This tutorial shows you how to transition a menu in and out of view using a randomly selected type of HTML+TIME (Timed Interactive Multimedia Extensions) transition. Some scripting is used to enable the user to activate the transition by opening and closing the menu and dynamically changing the type of transition applied to the menu. This is an example of integrating scripting with HTML+TIME transitions to make applications that utilize more dynamic transitions. Click the following Show Me button to see the application that is created in this tutorial.

Code example: http://samples.msdn.microsoft.com/workshop/samples/author/behaviors/htmltime/transitions/tutorialSamples/menu_example_tut.htm

The following topics are discussed in this document.

  • Prerequisites 
  • A Quick Look at the Code and Tutorial Outline 
  • Setting the Stage 
  • Setting Up the Menu 
  • Setting Up the BODY tag 
  • Opening the Menu 
  • Closing the Menu 
  • Summary 
  • Related Topics

Prerequisites

This article assumes you know how to use Introduction to DHTML Behaviors, specifically, the time2 behavior of HTML+TIME. This article does not go into great detail on how to add a behavior to your page nor does it cover how to declare a namespace and use custom tags, as required by the time2 behavior. These topics are covered in the HTML+TIME Overview and Spice Up Your Web Pages with HTML+TIME. For some understanding of HTML+TIME transitions, see Using HTML+TIME Transitions. In addition, you are expected to be familiar with Dynamic HTML (DHTML).

A Quick Look at the Code and Tutorial Outline

To start, have a quick look at the code of the entire sample. Later on, this code is explained in greater detail, but for now, it might be useful to have a preview of what is needed to make the tutorial sample.


<HTML xmlns:t = "urn:schemas-microsoft-com:time">
<HEAD>
<STYLE>
    .time {behavior: url(#default#time2);}
    .menu {height:45;font-size:14pt;}
    .inner {padding:10;padding-left:30;}
    #oContainer
    {
    background:threedface;
    cursor:hand; 
    border-style:outset;
    width:200;
    height:180;
    position:absolute; 
    }
    #oButton
    {
    position:absolute; 
    top:40;
    left:40;
    width:200;
    height:30;
    text-align:center;
    } 
</STYLE>
<SCRIPT>
// Function applies transition types to menu as well as begin the menu 
// and transitionFilters associated with it on the time line.
function fnShowMenu()
{
    // Set x and y coordinates of the absolutely positioned menu DIV.
    oContainer.style.left = event.x;
    oContainer.style.top = event.y;
    // The code inside this if block is used to display the menu.
    if (oContainer.currTimeState.isActive == false)
    {
        // The oTransitionInfo object is created, which has random values
        // for properties created by the fnSetType function.
        var oTransitionInfo = fnSetType();  
        // Assign values to TYPE and SUBTYPE properties of "in" TRANSITIONFILTER.
        oTran.type = oTransitionInfo.type;
        oTran.subType = oTransitionInfo.SubType
        // Begins Menu container DIV on the time line.
        oContainer.beginElement();
    }
    return false;
}
// The purpose of this function is to apply a new TYPE and SUBTYPE to the 
// TRANSITIONFILTER element that transitions the menu out of view. 
function fnHideMenu()
{
    if (oContainer.currTimeState.isActive == true)
    {
        var oTransitionInfo = fnSetType();
        oTranOut.type = oTransitionInfo.type;
        oTranOut.subType = oTransitionInfo.subType;
    }
}

// This function returns random values to assign to the TYPE and SUBTYPE 
// properties.
function fnSetType()
{
    // Create a random value between 1 and 10
    var randomNumber = (Math.random() * 10);
    randomNumber = Math.ceil(randomNumber);
    var sType, sSubType;
    switch(randomNumber)
    {
        case 1:
            sType = "barWipe";
            sSubType = "topToBottom";
            break;
        case 2:
            sType = "barnDoorWipe";
            sSubType = "horizontal";       
            break;
        case 3:
            sType = "barnDoorWipe";      
            break;
        case 4:
            sType = "snakeWipe";      
            break;
        case 5:
            sType = "irisWipe";     
            break;
        case 6:
            sType = "pushWipe";     
            break;
        case 7:
            sType = "ellipseWipe";      
            break;
        case 8:
            sType = "fanWipe";      
            break;
        case 9:
            sType = "fade";      
            break;
        case 10:
            sType = "clockWipe";      
            break;
    }
    // Create the object that will store the values that will be returned.
    var oTransitionInfo = new Object();
    oTransitionInfo.type = sType;
    oTransitionInfo.subType = sSubType;
    return oTransitionInfo;
}
// Hilite function is only used to hilite menu selections on mousover.
function Hilite(obj)
{
    obj.style.color = "highlighttext";
    obj.style.background = 'highlight';
    obj.style.border = '1px solid threedhighlight';
    obj.style.borderTop = '1px solid threedshadow';
    obj.style.borderLeft = '1px solid threedshadow';
}
// Restore function is only used to undo the effects of the Hilite function
// on mouseleave. 
function Restore(obj)
{
    obj.style.color  = '';
    obj.style.background  = 'threedface';
    obj.style.border = '1px solid threedface';
}
</SCRIPT>
<?import namespace = t urn = "urn:schemas-microsoft-com:time" 
implementation = "#default#time2" />
</HEAD>
<BODY onclick="fnHideMenu();" oncontextmenu="return fnShowMenu()" style="background-color:slategray" ID="oBody">

<!-- This DIV (the menu) ends .3 seconds after the document body is clicked. 
as specified by the END attribute.  -->
<DIV ID="oContainer" CLASS="time" BEGIN="indefinite" END="oBody.click+.3"">
<t:TRANSITIONFILTER ID="oTran" BEGIN="oContainer.begin" DUR=".3"/>
<t:TRANSITIONFILTER ID="oTranOut" MODE="out"  BEGIN="oContainer.end - .3" DUR=".3"/>

<DIV onmouseover="Hilite(this);" onmouseout="Restore(this);" CLASS="menu" >
<DIV CLASS="inner">Selection 1</DIV>
</DIV>

<DIV onmouseover="Hilite(this);" onmouseout="Restore(this);"  CLASS="menu" >
<DIV CLASS="inner">Selection 2</DIV>
</DIV>

<div onmouseover="Hilite(this);" onmouseout="Restore(this);" CLASS="menu" >
<DIV CLASS="inner">Selection 3</DIV>
</DIV>

<div onmouseover="Hilite(this);" onmouseout="Restore(this);" CLASS="menu" >
<DIV class="inner">Selection 4</DIV>
</DIV>

</DIV>

</BODY>
</HTML>

Much of the preceding code is inside of the STYLE block. This formatting code is important for this sample to render correctly but is separated from the functional elements for clarity. The rest of the code consists of the elements that make up the menu and the script that provides the menu functionality. This tutorial walks through each of the following steps to create the sample.

  1. Write the miscellaneous code needed for the sample to work properly.
    • Write the code that is needed to instantiate the HTML+TIME behavior.
    • Write the formatting code that will be needed by the menu in a STYLE tag.
    • Create a function that facilitates the menu mouseover effects.
    • Create a function that returns randomly chosen values for a transition type. This function is used by other functions of the sample to apply random transitions to the menu as it transitions in and out of view.
  2. Create the menu.
  3. Create the function to facilitate the opening of the menu with a transition.
  4. Create the function to facilitate the closing of the menu with a transition.

Setting the Stage

There is a substantial amount of miscellaneous code needed to support the functional elements and script of the sample. The code presented in this section allows the code in subsequent sections to work correctly and for element formatting.

  1. Allow HTML+TIME behavior.

    To use HTML+TIME elements and the time2 behavior, the following code is needed.

    
    <HTML XMLNS:t = "urn:schemas-microsoft-com:time">
    <HEAD>
      <STYLE>
        .time    {behavior: url(#default#time2);}
      </STYLE>
      <?import namespace = t urn = "urn:schemas-microsoft-com:time" 
        implementation = "#default#time2" />
    </HEAD>
    <BODY>
    .
    .
    .
    </BODY>
    </HTML>
    

    For more information about creating an XML namespace and referencing the time2 behavior, see Authoring HTML+TIME.

  2. Format the elements.

    Next, we add the formatting code inside of the STYLE tags. All of the elements in this sample derive their formatting from here. By separating formatting code from rendered elements, the functional code is simplified.

    
    <STYLE>
        .time {behavior: url(#default#time2);}
        .menu {height:45;font-size:14pt;}
        .inner {padding:10;padding-left:30;}
        #oContainer
        {
        background:threedface;
        cursor:hand; 
        border-style:outset;
        width:200;
        height:180;
        position:absolute; 
        }
        #oButton
        {
        position:absolute; 
        top:40;
        left:40;
        width:200;
        height:30;
        text-align:center;
        } 
    </STYLE>
    
  3. Highlight the menu selection buttons.

    A few functions are used to handle the onmouseover and onmouseout events bound to the selection DIVs of the menu that is shown later. This makes the selection buttons highlight and return to normal as the cursor moves over them.

    
    <SCRIPT>
    // fnHilite function is only used to hilite menu selections on mousover.
    function fnHilite(obj)
    {
        obj.style.color = "highlighttext";
        obj.style.background = 'highlight';
        obj.style.border = '1px solid threedhighlight';
        obj.style.borderTop = '1px solid threedshadow';
        obj.style.borderLeft = '1px solid threedshadow';
    }
    // Restore function is only used to undo the effects of the fnHilite function
    // on mouseleave. 
    function fnRestore(obj)
    {
        obj.style.color  = '';
        obj.style.background  = 'threedface';
        obj.style.border = '1px solid threedface';
    }
    </SCRIPT>
    
  4. Generate random values.

    One function is used to generate random values that are returned for assignment to the type and subtype properties of the t:TRANSITIONFILTER elements. This function is called by the fnShowMenu() and fnHideMenu() functions, created later in this tutorial.

    
    // This function returns random values to assign to the TYPE and SUBTYPE 
    // properties.
    function fnSetType()
    {
        // Create a random value between 1 and 10
        var randomNumber = (Math.random() * 10);
        randomNumber = Math.ceil(randomNumber);
        var sType, sSubType;
        switch(randomNumber)
        {
            case 1:
                sType = "barWipe";
                sSubType = "topToBottom";
                break;
            case 2:
                sType = "barnDoorWipe";
                sSubType = "horizontal";       
                break;
            case 3:
                sType = "barnDoorWipe";      
                break;
            case 4:
                sType = "snakeWipe";      
                break;
            case 5:
                sType = "irisWipe";     
                break;
            case 6:
                sType = "pushWipe";     
                break;
            case 7:
                sType = "ellipseWipe";      
                break;
            case 8:
                sType = "fanWipe";      
                break;
            case 9:
                sType = "fade";      
                break;
            case 10:
                sType = "clockWipe";      
                break;
        }
        // Create the object that stores the values that are returned.
        var oTransitionInfo = new Object();
        oTransitionInfo.type = sType;
        oTransitionInfo.subType = sSubType;
        return oTransitionInfo;
    }
    

Setting Up the Menu

The menu consists of a container DIV with several inner DIVs. The inner DIVs are the selection buttons of the menu. The container DIV has the t:TRANSITIONFILTER elements applied to it. The inner DIVs transition in and out of view with the container DIV.


<DIV ID="oContainer" CLASS="time" BEGIN="indefinite" END="oBody.click+1"">
<t:TRANSITIONFILTER ID="oTran" BEGIN="oContainer.begin" DUR="1"/>
<t:TRANSITIONFILTER ID="oTranOut" MODE="out"  BEGIN="oContainer.end - 1" DUR="1"/>

<DIV onmouseover="fnHilite(this);" onmouseout="fnRestore(this);" CLASS="menu" >
<DIV CLASS="inner">Selection 1</DIV>
</DIV>

<DIV onmouseover="fnHilite(this);" onmouseout="fnRestore(this);"  CLASS="menu" >
<DIV CLASS="inner">Selection 2</DIV>
</DIV>

<div onmouseover="fnHilite(this);" onmouseout="fnRestore(this);" CLASS="menu" >
<DIV CLASS="inner">Selection 3</DIV>
</DIV>

<DIV onmouseover="fnHilite(this);" onmouseout="fnRestore(this);" CLASS="menu" >
<DIV class="inner">Selection 4</DIV>
</DIV>

</DIV>

The container DIV makes up the body of the menu. It is the element that has the t:TRANSITIONFILTER elements applied to it and transitions in and out of view.


<DIV ID="oContainer" CLASS="time" BEGIN="indefinite" END="oBody.click + .3">
<t:TRANSITIONFILTER ID="oTran" BEGIN="oContainer.begin" DUR=".3"/>
<t:TRANSITIONFILTER ID="oTranOut" MODE="out"  BEGIN="oContainer.end - .3" DUR=".3"/>
</DIV>

The BEGIN attribute is set to indefinite value for the container DIV. This is because the container DIV is started on the time line through script, see the Opening the Menu section found later in this tutorial. The t:TRANSITIONFILTER with an ID attribute value oTran transitions the menu into view. It has a BEGIN attribute value of oContainer.begin, which ties the beginning of this t:TRANSITIONFILTER to the beginning time of the container DIV. The END attribute of the container DIV is set to oBody.click + .3. This is the mechanism by which the menu closes. This means that the menu container DIV ends on the time line .3 seconds after the body of the document is clicked on. The .3 second delay is important to allows the t:TRANSITIONFILTER element with a MODE attribute value of out time to transition the menu out. Notice that the t:TRANSITIONFILTER has a DUR value of .3 seconds.

The inner DIVs make up the selection buttons of the menu. The DIVs change style properties as the mouse pointer moves over and off of them using the fnHilite() and fnRestore() functions that were shown earlier. These DIVs transition in and out of view with the container DIV. Here are the inner DIVs inside of the container DIV.


<DIV ID="oContainer" CLASS="time" BEGIN="indefinite" END="oBody.click+1"">
<t:TRANSITIONFILTER ID="oTran" BEGIN="oContainer.begin" DUR="1"/>
<t:TRANSITIONFILTER ID="oTranOut" MODE="out" BEGIN="oContainer.end - 1" DUR="1"/>

<DIV onmouseover="fnHilite(this);" onmouseout="fnRestore(this);" CLASS="menu" >
<DIV CLASS="inner">Selection 1</DIV>
</DIV>

<DIV onmouseover="fnHilite(this);" onmouseout="fnRestore(this);"  CLASS="menu" >
<DIV CLASS="inner">Selection 2</DIV>
</DIV>

<div onmouseover="fnHilite(this);" onmouseout="fnRestore(this);" CLASS="menu" >
<DIV CLASS="inner">Selection 3</DIV>
</DIV>

<DIV onmouseover="fnHilite(this);" onmouseout="fnRestore(this);" CLASS="menu" >
<DIV class="inner">Selection 4</DIV>
</DIV>

</DIV>

Setting Up the BODY tag

The user opens the menu when the user right-clicks the BODY of the document and closes the menu when the user left-clicks the document BODY. Events bound to the BODY tag accomplish this.


<BODY ID="oBody" oncontextmenu="return fnShowMenu()" onclick="fnHideMenu();"  
STYLE="background-color:slategray">

As you can see, two different functions are called, depending on whether the user left-clicks or right-clicks the BODY. These functions control the way the menu transitions in and out of view. Next, we see how these functions work.

Opening the Menu

The fnShowMenu() function starts the container DIV (the menu) on the time line. In addition, randomly determined transition type and subtype property values are assigned to the oTran t:TRANSITIONFILTER element, which is responsible for transitioning the container DIV into view. This function is called when the user right-clicks the BODY of the document.


// Function applies transition types to menu as well as begin the menu 
// and "in" TRANSITIONFILTER associated with it on the time line.
function fnShowMenu()
{
    // Updates the x and y coordinates of the absolutely positioned menu DIV.
    oContainer.style.left = event.x;
    oContainer.style.top = event.y;
    // Check to see if the menu is not active on the time line.
    if (oContainer.currTimeState.isActive == false)
    {
        // The oTransitionInfo object is created, which has random values
        // for properties created by the fnSetType function.
        var oTransitionInfo = fnSetType();  
        // Assign values to TYPE and SUBTYPE properties of "in" TRANSITIONFILTER.
        oTran.type = oTransitionInfo.type;
        oTran.subType = oTransitionInfo.SubType
        // Begins Menu container DIV on the time line.
        oContainer.beginElement();
    }
    return false;
}
  1. First, the x and y coordinates where the menu will be displayed is set to the location of the mouse pointer, relative to the document when the user right-clicks.

    
    function fnShowMenu()
    {
        // Updates the x and y coordinates of the absolutely positioned menu DIV.
        oContainer.style.left = event.x;
        oContainer.style.top = event.y;
    }
    
  2. Next, a check is made to make sure that the menu is not currently active on the time line. This, in effect, checks to see if the menu is currently visible. If it is not visible, then the code that opens the menu is executed.

    
    function fnShowMenu()
    {
        // Updates the x and y coordinates of the absolutely positioned menu DIV.
        oContainer.style.left = event.x;
        oContainer.style.top = event.y;
        // Check to see if the menu is not active on the time line.
        if (oContainer.currTimeState.isActive == false)
        {
            // Code that will open the menu
        }
    }
    
  3. The oTransitionInfo object is assigned random values from the fnSetType() function, shown earlier.

    
    function fnShowMenu()
    {
        // Updates the x and y coordinates of the absolutely positioned menu DIV.
        oContainer.style.left = event.x;
        oContainer.style.top = event.y;
        // Check to see if the menu is not active on the time line.
        if (oContainer.currTimeState.isActive == false)
        {
            // The oTransitionInfo object is created, which has random values
            // for properties created by the fnSetType function.
            var oTransitionInfo = fnSetType();  
            .
            .
        }
    }
    
  4. Random values are assigned to the "in" t:TRANSITIONFILTER (oTran) type and subtype properties.

    
    function fnShowMenu()
    {
        // Updates the x and y coordinates of the absolutely positioned menu DIV.
        oContainer.style.left = event.x;
        oContainer.style.top = event.y;
        // Check to see if the menu is not active on the time line.
        if (oContainer.currTimeState.isActive == false)
        {
            // The oTransitionInfo object is created, which has random values
            // for properties created by the fnSetType function.
            var oTransitionInfo = fnSetType();  
            // Assign values to TYPE and SUBTYPE properties of "in" TRANSITIONFILTER.
            oTran.type = oTransitionInfo.type;
            oTran.subType = oTransitionInfo.SubType;
            .
            .
        }
    }
    
  5. The container DIV is started on the time line using the beginElement method. Recall that the "in" t:TRANSITIONFILTER begins when the container DIV begins on the time line; for more about this, see the Setting Up the Menu section. In addition, this t:TRANSITIONFILTER has new, randomly generated values for its type and subtype properties. This makes the menu transition into view in a random way each time it is opened.

    
    function fnShowMenu()
    {
        // Updates the x and y coordinates of the absolutely positioned menu DIV.
        oContainer.style.left = event.x;
        oContainer.style.top = event.y;
        // Check to see if the menu is not active on the time line.
        if (oContainer.currTimeState.isActive == false)
        {
            // The oTransitionInfo object is created, which has random values
            // for properties created by the fnSetType function.
            var oTransitionInfo = fnSetType();  
            // Assign values to TYPE and SUBTYPE properties of "in" TRANSITIONFILTER.
            oTran.type = oTransitionInfo.type;
            oTran.subType = oTransitionInfo.SubType
            // Begins Menu container DIV on the time line.
            oContainer.beginElement();
        }
        return false;
    }
    

    Note  The function refurns false so that the default context menu for Internet Explorer is not shown.

Closing the Menu

Closing the menu is accomplished by clicking anywhere on the body of the document including on the menu itself. As you remember, the container DIV ends .3 seconds after the body is clicked.


<DIV ID="oContainer" END="oBody.click + .3" . . .>

In addition, the t:TRANSITIONFILTER associated with the container DIV with a MODE value of out, begins .3 seconds before the DIV ends.

<t:TRANSITIONFILTER ID="oTranOut" MODE="out"  BEGIN="oContainer.end - .3" DUR=".3"/>

This would be enough to close the menu with a transition. However, this sample has the menu transition out with a random transition. For this, it is necessary to have one more function. This function is called when the body of the document is clicked on.


<BODY ID="oBody" oncontextmenu="return fnShowMenu()" onclick="fnHideMenu();"  
STYLE="background-color:slategray">

The fnHideMenu() function is very similar to the fnShowMenu() function, except that it is the t:TRANSITIONFILTER with MODE set to out that is having its type and subtype properties changed.


// The purpose of this function is to apply a new TYPE and SUBTYPE to the 
// TRANSITIONFILTER element that transitions the menu out of view. 
function fnHideMenu()
{
    if (oContainer.currTimeState.isActive == true)
    {
        var oTransitionInfo = fnSetType();
        oTranOut.type = oTransitionInfo.type;
        oTranOut.subType = oTransitionInfo.subType;
    }
}

Summary

You should now have created something similar to the application shown in the introduction of this tutorial. As you have seen, control of the HTML+TIME transitions was specified in both the attributes of the t:TRANSITIONFILTER elements themselves and through scripting. This is only one example of how integrating scripting with HTML+TIME can extend the ability of your Web pages.