Making Webpages More Accessible
Daniel Odievich
Microsoft Corporation
November 2000
Summary: This article offers practical tips for making webpages more accessible, including accommodating users who are unable to use or have difficulty using a mouse. (5 printed pages)
- Introduction
- What's Wrong with the Mouse?
- Definitions
- Using Native HTML 3.2 Elements
- Using the LABEL Object
- Using the TITLE Attribute
- Using DHTML Elements and Events
- Example 1: Anchor Navigation Cancel
- Example 2: Table Cell Navigation
- Example 3: Calculator Application
Introduction
In today's web world, it is essential to provide clean and accessible web applications for the exponentially increasing number of users who go online every day. By using the techniques described in this article, with minimal effort you will be able to make webpages more accessible to people with disabilities.
Most of us remember times when a graphical user interface (GUI) operating system was just wishful thinking. The world was driven by text-based interfaces. Everything you wanted to do with a computer program could be done with a keyboard; and a mouse was an unwieldy two-kilogram device more of a novelty than of any real use.
Things sure have changed since then. Almost every program allows you to click hyperlinks to connect to websites. DHTML allows unprecedented flexibility and richness in GUI applications. Things are looking good on the Net!
What's Wrong with the Mouse?
I am a die-hard "keyboarder." If there is a keyboard shortcut in Microsoft Windows, I know it. The only time I use the mouse is to play Half-Life or to navigate keyboard-unfriendly websites. It is my conscious choice not to use the mouse, but for millions of other people, including my mother who has carpal tunnel syndrome, using the mouse is either painful or impossible. If you'd like these people to use your site, it is essential that you keyboard-enable your webpages. In this article, I will explain how you can make this happen easily by using Internet Explorer and DHTML.
Definitions
The following terms will be used in this article:
Term | Description |
---|---|
Focus |
Position of the cursor on the webpage, indicated either by the cursor or by a light gray marquee. |
tabIndex, tabOrder |
Order of focus movement between elements on the webpage when the TAB key is pressed. If two elements on the page have the same tabIndex, the element that occurs first in HTML source code will be the first recipient of the focus. |
Access key |
Button that is pressed in combination with the ALT key. When the ALT+<access key> combination is received, the focus is moved to the element of the webpage that uses this access key. |
Using Native HTML 3.2 Elements
You will save time and work if your site uses the standard HTML elements listed in the following table. These elements are accessible because users can access them by using the TAB key, without any modification to your webpage.
Table 1. Native HTML 3.2 elements that support accessibility
Name | Notes |
---|---|
a, img | Anchor tags always act as a TAB stop. Sometimes it is useful to use the <a> tag as a substitute for a button. To stop the automatic browser navigation when the anchor is clicked, set the returnValue property of the window.event object to False. See Example 1: Anchor navigation cancel |
area | This tag allows you to define areas receiving focus on a complex image map. |
button, input, select, textarea | Default form UI elements, which act as TAB stops. |
frame, frameset, iframe | Frames can also act as TAB stops. Once the focus has entered the frame, subsequent tabbing will enumerate contents before exiting to the next frame. |
Other standard HTML elements can be made accessible by adding tabIndex and accessKey attributes to HTML tags.
Table 2. HTML elements that can be made accessible
Name | Notes |
---|---|
applet, embed, object | These tags enable the active content (Microsoft ActiveX controls, Java applets) on the webpage. You can enable keyboard navigation by assigning the tabIndex attribute to these tags. However, the tabIndex order is altered when an ActiveX control or Java applet exposes its own accessKey-enabled UI; the focus will enter the ActiveX control and will follow the order defined by the developer of that control. |
body | You can give your whole document a valid tabIndex and accessKey attribute. |
marquee | The MARQUEE object can also act as a receiver of the index, making it an attractive multimedia alternative to the regular form INPUT button. |
table, td, th | Applying accessKey and tabIndex attributes to these tags enables the user to navigate through a table like an Excel spreadsheet! See Example 2: Table cell navigation |
Using the LABEL Object
HTML 4.0 specifications introduce the LABEL object, which you can use to associate text with any other HTML object or intrinsic control. Linked LABEL and HTML objects act identically when causing and receiving events, whether the user clicks the LABEL or the HTML object. To link the LABEL and HTML objects, set the FOR attribute of the LABEL equal to the ID attribute of the HTML object.
The following example associates the LABEL control with the text box. When the user clicks the LABEL control or presses the ALT+T key combination the LABEL control sets the focus to the text box.
<LABEL FOR="txtInputBox"><U>T</U>ype value:</LABEL>
<INPUT TYPE="text" ID="txtInputBox" NAME="txtInputBox" ACCESSKEY="t">
Using the TITLE Attribute
Tooltips are useful in making the UI more attractive for people with good vision and a mouse at their disposal, as well as for blind people. The TITLE attribute tag makes it very easy to teach a complex user interface, without cluttering the page with visible text. It can be used by screen-reading software to provide the same tooltip information that would appear when the mouse hovers over any DHTML object. This enables webpage navigation for the blind:
<LABEL FOR="txtInputBox" TITLE="Click ALT-T to set focus to very important
text box on the right"><U>T</U>ype value:</LABEL>
<INPUT TYPE="text" ID="txtInputBox" NAME="txtInputBox" ACCESSKEY="t"
TITLE="Type some very important value in this textbox">
Using DHTML Elements and Events
Of course, in today's web world, it is difficult to build a good-looking application using only plain vanilla HTML 3.2 elements. DHTML is used in every interactive website in the world. Almost every DHTML element can be enabled for accessibility by using the tabIndex and accessKey attributes. Additional functionality can be gained with the use of DHTML keyboard events such as onkeypress, onkeyup, and onkeydown. The differences between these events can be seen in the following table. Table 3 illustrates how to use onkeypress, onkeyup, and onkeydown events together to process most of the user's input with a minimal amount of code.
Table 3. DHTML keyboard events
Name | Notes |
---|---|
onkeypress | This event occurs when the user presses and releases any alphanumeric key. System buttons, such as arrow and function keys, are not recognized. |
onkeyup | This event occurs when the user releases any keyboard key that was previously depressed. |
onkeydown | This event occurs when the user presses any keyboard key, including system buttons such as arrow and function keys. |
Example 1: Anchor Navigation Cancel
<HTML>
<HEAD>
<TITLE>
Example 1 - Anchor Navigation Cancel
</TITLE>
<xml>
<MSHelp:Keyword Index="A" Term="Example1-AnchorNavigationCancel"/>
</xml>
</HEAD>
<SCRIPT LANGUAGE="VBSCRIPT">
'Turn on Option Explicit, VB programmer's best friend
Option Explicit
'--------------------------------------------------------
Function PreventNavigate()
'Cancel navigation to http://www.yahoo.com
window.event.returnValue = False
'Do client-side scripting work instead
MsgBox "Sorry, your navigation away from this page has been canceled. Instead of surfing, let's do some client-side work!", vbInformation, "Example"
End Function
'--------------------------------------------------------
</SCRIPT>
<BODY>
<!--TOOLBAR_START-->
<!--TOOLBAR_EXEMPT-->
<!--TOOLBAR_END-->
Example 1 - Anchor Navigation Cancel <BR>
(View source for code)
<P>
You can click or tab to the anchor...<BR>
<!--
Normally, this anchor will navigate to Yahoo. However, ONCLICK event handler
PreventNavigate() cancels navigation, allowing to do client-side scripting work.
Thus, anchor could be easily used as a button
-->
[Click Me!](http://www.yahoo.com)
<P>
... Or anchor-enabled image...<BR>
<!--
Obviously, buttons could be implemented as images
-->
[<IMG SRC="go.gif" TITLE="No, click me and see what happens!">](http://www.yahoo.com)
</BODY>
</HTML>
Example 2: Table Cell Navigation
<HTML>
<HEAD>
<TITLE>
Example 2 - Table
</TITLE>
<xml>
<MSHelp:Keyword Index="A" Term="Example2-Table"/>
</xml>
</HEAD>
<SCRIPT LANGUAGE="VBSCRIPT">
'Turn on Option Explicit, VB programmer's best friend
Option Explicit
Dim mobjCellCurrent
'--------------------------------------------------------
'this function retrieves currently selected cell value
Function GetCellValue()
Dim sTagNane
'get tag of object this event was caused by
sTagName = LCase(window.event.srcElement.tagName)
Select Case sTagName
Case "td", "th", "tf"
'if this is a TD, TH, or TF tag,
txtCurrentValue.value = window.event.srcElement.innerText
'remember currently selected table cell in module-level variable
Set mobjCellCurrent = window.event.srcElement
Case Else
'Do nothing
End Select
End Function
'--------------------------------------------------------
'--------------------------------------------------------
'this function puts new value in currently selected cell
Function SetCellValue()
On Error Resume Next
If Not IsNothing(mobjCellCurrent) Then
'if there is a valid currently selected table cell, set it's innerText
'property to value in textbox
mobjCellCurrent.innerText = txtCurrentValue.value
mobjCellCurrent.focus
End If
If Not Err.Number = 0 Then Err.Clear
End Function
'--------------------------------------------------------
'--------------------------------------------------------
'this is page-wide keypress processing function
Function ProcessKeyPress()
Dim lKeyCode
Dim sTagNane
sTagName = LCase(window.event.srcElement.tagName)
lKeyCode = window.event.keyCode
'MsgBox "lKeyCode=" & lKeyCode & "; sTagName=" & sTagName
Select Case sTagName
Case "td", "th", "tf"
'if there is any keyboard input and we're inside one of the cells,
'start editing them
txtCurrentValue.focus
Case "input"
'if we're inside editing textbox
If window.event.srcElement.id = "txtCurrentValue" Then
'and user pressed ENTER
If lKeyCode = 13 then
'save values to the cell
Call SetCellValue()
End If
End If
Case Else
'Do nothing
End Select
End Function
'--------------------------------------------------------
'--------------------------------------------------------
'this is page-wide keyup processing function
Function ProcessKeyUp()
Dim lKeyCode
Dim sTagNane
sTagName = LCase(window.event.srcElement.tagName)
lKeyCode = window.event.keyCode
'MsgBox "lKeyCode=" & lKeyCode & "; sTagName=" & sTagName
Select Case sTagName
Case "td", "th", "tf"
'if there is any keyboard input and we're inside one of the cells,
'start editing them
'113=F2
If lKeyCode = 113 Then
txtCurrentValue.focus
End IF
Case "input"
'if we're inside editing textbox
If window.event.srcElement.id = "txtCurrentValue" Then
'and user pressed ENTER
If lKeyCode = 113 then
'set focus back to cell
mobjCellCurrent.focus
End If
End If
Case Else
'Do nothing
End Select
End Function
'--------------------------------------------------------
'--------------------------------------------------------
Sub Window_OnLoad()
txtCurrentValue.focus
End Sub
'--------------------------------------------------------
</SCRIPT>
<BODY ONKEYPRESS="ProcessKeyPress()" ONKEYUP="ProcessKeyUp()">
<!--TOOLBAR_START-->
<!--TOOLBAR_EXEMPT-->
<!--TOOLBAR_END-->
Example 2 - Table <BR>
(View source for code)
<P>
<!--
We're using labels and accesskeys here to enable keyboard navigation to editing
textbox and table
-->
<LABEL FOR="txtCurrentValue" TITLE="You can press ALT-C to set focus to this
LABEL and consequently to textbox to the right"><U>C</U>urrently selected
cell:</LABEL>
<INPUT TYPE="text" ID="txtCurrentValue" NAME="txtCurrentValue" TABINDEX=1
ACCESSKEY="c" TITLE="Value of currently selected cell. You can put new value
here and click 'Set' button to change it in table">
<INPUT TYPE="button" ID="cmdSetValue" NAME="cmdSetValue" TABINDEX=1 ACCESSKEY="s"
VALUE="Set" ONCLICK="SetCellValue()" TITLE="Click this button to set value of
selected cell below">
<P>
<LABEL FOR="tblSample" TITLE="You can press ALT-T to set focus to this LABEL
and consequently to table below"><U>T</U>able (select cell, press ENTER to
edit value in cell):</LABEL>
<TABLE BORDER=1 TABINDEX=2 ID="tblSample" ACCESSKEY="t" TITLE="You can use
TAB key to navigate between cells of this table">
<THEAD>
<TR>
<TH ONFOCUS="GetCellValue" TABINDEX=3>Heading 1</TH>
<TH ONFOCUS="GetCellValue" TABINDEX=3>Heading 2</TH>
</TR>
</THEAD>
<TBODY>
<TR>
<TD ONFOCUS="GetCellValue" TABINDEX=4>Row 1, Column 1 text.</TD>
<TD ONFOCUS="GetCellValue" TABINDEX=4>Row 1, Column 2 text.</TD>
</TR>
<TR>
<TD ONFOCUS="GetCellValue" TABINDEX=5>Row 2, Column 1 text.</TD>
<TD ONFOCUS="GetCellValue" TABINDEX=5>Row 2, Column 2 text.</TD>
</TR>
</TBODY>
<TFOOT >
<TR>
<TD ONFOCUS="GetCellValue" TABINDEX=6 COLSPAN=2 ALIGN="center">
This text is in the table footer.
</TD>
</TR>
</TFOOT>
</TABLE>
</BODY>
</HTML>
Example 3: Calculator Application
As a sample illustrating usage of the DHTML keyboard events just described, I wrote a web calculator sample application. It is fully accessible via the keyboard through using a mix of the accessKey and tabIndex attributes, onkeyup, onkeypress, and onkeydown events, and the LABEL object.
<HTML>
<HEAD>
<TITLE>
Example 3 - Calculator
</TITLE>
<xml>
<MSHelp:Keyword Index="A" Term="Example3-Calculator"/>
</xml>
<STYLE>
TABLE
{
BACKGROUND: #c0c0c0;
FONT-SIZE: 1em;
}
TD
{
FONT-SIZE: 1em;
FONT-FAMILY: Verdana, Arial, Helvetica;
FONT-WEIGHT: normal;
TEXT-DECORATION: none;
TEXT-ALIGN: center
}
TH
{
FONT-SIZE: 1em;
FONT-FAMILY: Verdana, Arial, Helvetica;
FONT-WEIGHT: bold;
TEXT-DECORATION: none;
TEXT-ALIGN: center
}
.clsDigit
{
BACKGROUND: #ffffff;
COLOR: black;
FONT-FAMILY: Courier;
FONT-WEIGHT: bold;
FONT-SIZE: 1em;
}
.clsOperand
{
BACKGROUND: #c0c0c0;
COLOR: black;
FONT-FAMILY: Courier;
FONT-WEIGHT: bold;
FONT-SIZE: 1em;
}
.clsClear
{
BACKGROUND: #008080;
COLOR: black;
FONT-FAMILY: Courier;
FONT-WEIGHT: bold;
FONT-SIZE: 1em;
}
</STYLE>
</HEAD>
<SCRIPT LANGUAGE="VBSCRIPT">
'Turn on Option Explicit, VB programmer's best friend
Option Explicit
'--------------------------------------------------------
'ASCII codes for keys that matter
Const vbKey0 = &H30
Const vbKey1 = &H31
Const vbKey2 = &H32
Const vbKey3 = &H33
Const vbKey4 = &H34
Const vbKey5 = &H35
Const vbKey6 = &H36
Const vbKey7 = &H37
Const vbKey8 = &H38
Const vbKey9 = &H39
Const vbKeyNumpad0 = &H30
Const vbKeyNumpad1 = &H31
Const vbKeyNumpad2 = &H32
Const vbKeyNumpad3 = &H33
Const vbKeyNumpad4 = &H34
Const vbKeyNumpad5 = &H35
Const vbKeyNumpad6 = &H36
Const vbKeyNumpad7 = &H37
Const vbKeyNumpad8 = &H38
Const vbKeyNumpad9 = &H39
Const vbKeyDecimalGeneral = &H2E
Const vbKeyMultiplyGeneral = &H2A
Const vbKeyDivideGeneral = &H2F
Const vbKeySubtractGeneral = &H2D
Const vbKeyAddGeneral = &H2B
Const vbKeyDecimalNumLock = &H6E
Const vbKeyMultiplyNumLock = &H6A
Const vbKeyDivideNumLock = &H6F
Const vbKeySubtractNumLock = &H6D
Const vbKeyAddNumLock =&H6B
Const vbKeyF9 = &H78
Const vbKeyEscape = &H1B
Const vbKeyC = &H43
Const vbKeyCSmall = &H63
Const vbKeyE = &H45
Const vbKeyESmall = &H65
Const vbKeyEqual = &H3D
Const vbKeyEnter = &HD
'--------------------------------------------------------
'Types of operations that could be performed
Const calcOperandNone = 0
Const calcOperandMultiply = 1
Const calcOperandDivide = 2
Const calcOperandSubtract = 3
Const calcOperandAdd = 4
'Current calculator state
Const calcStateUnknown = 0
Const calcStateTypingFirstNumber = 1
Const calcStateTypingOperand = 2
Const calcStateTypingSecondNumber = 3
Const calcStatePerformedOperation = 4
Dim mCurrentState 'as one of the calcState constants
Dim mcurrPreviousValue 'as Currency Variant
Dim mcurrCurrentValue 'as Currency Variant
Dim mCurrentOperand 'one of the calcOperand constants
Dim mbPeriodPressed 'whether Decimal period has been pressed
Dim mlNumberOfDecimalPlaces 'how many decimal places we have in this number
'--------------------------------------------------------
'this is page-wide keypress processing function
Function ProcessKeyPress()
Dim s
Dim lKeyCode
On Error Resume Next
lKeyCode = window.event.keyCode
txtKeyCodeKeyPress.value = lKeyCode
txtHexKeyCodeKeyPress.value = "&H" & Hex(lKeyCode)
txtCtrlKeyPress.value = window.event.ctrlKey
txtAltKeyPress.value = window.event.altKey
txtShiftKeyPress.value = window.event.shiftKey
s = LCase(window.event.srcElement.tagName)
txtTagNameKeyPress.value = s
s = window.event.srcElement.id
If Not Err.Number = 0 Then
'Element has no ID
Err.Clear
s = ""
End If
txtIDKeyPress.value = s
If Not IsKeyPressInsideCalculator() Then
'KeyUp event did not originate from inside of divCalculator DIV, exit handler
Exit Function
End If
Select Case lKeyCode
Case vbKey0, vbKeyNumpad0
Call Press_0()
Case vbKey1, vbKeyNumpad1
Call Press_1()
Case vbKey2, vbKeyNumpad2
Call Press_2()
Case vbKey3, vbKeyNumpad3
Call Press_3()
Case vbKey4, vbKeyNumpad4
Call Press_4()
Case vbKey5, vbKeyNumpad5
Call Press_5()
Case vbKey6, vbKeyNumpad6
Call Press_6()
Case vbKey7, vbKeyNumpad7
Call Press_7()
Case vbKey8, vbKeyNumpad8
Call Press_8()
Case vbKey9, vbKeyNumpad9
Call Press_9()
Case vbKeyDecimalGeneral, vbKeyDecimalNumLock
Call Press_Period
Case vbKeyDivideGeneral, vbKeyDivideNumLock
Call Press_Divide()
Case vbKeyMultiplyNumLock, vbKeyMultiplyGeneral
Call Press_Multiply()
Case vbKeySubtractGeneral, vbKeySubtractNumLock
Call Press_Subtract()
Case vbKeyAddGeneral, vbKeyAddNumLock
Call Press_Add()
Case vbKeyF9
Call Press_F9()
Case vbKeyEscape
Call Press_Escape()
Case vbKeyC, vbKeyCSmall
Call Press_Clear()
Case vbKeyE, vbKeyESmall
Call Press_ClearError()
Case vbKeyEqual, vbKeyEnter
Call Press_Equal()
Case Else
'Do nothing
window.event.returnValue = Nothing
End Select
End Function
'--------------------------------------------------------
'--------------------------------------------------------
'this is page-wide keyup processing function
Function ProcessKeyUp()
Dim s
Dim lKeyCode
On Error Resume Next
lKeyCode = window.event.keyCode
txtKeyCodeKeyUp.value = lKeyCode
txtHexKeyCodeKeyUp.value = "&H" & Hex(lKeyCode)
txtCtrlKeyUp.value = window.event.ctrlKey
txtAltKeyUp.value = window.event.altKey
txtShiftKeyUp.value = window.event.shiftKey
s = LCase(window.event.srcElement.tagName)
txtTagNameKeyUp.value = s
s = window.event.srcElement.id
If Not Err.Number = 0 Then
'Element has no ID
Err.Clear
s = ""
End If
txtIDKeyUp.value = s
If Not IsKeyPressInsideCalculator() Then
'KeyUp event did not originate from inside of divCalculator DIV, exit handler
Exit Function
End If
Select Case lKeyCode
Case vbKeyF9
Call Press_ChangeSign()
Case Else
'Do nothing
window.event.returnValue = Nothing
End Select
End Function
'--------------------------------------------------------
'--------------------------------------------------------
'this is page-wide keydown processing function
Function ProcessKeyDown()
Dim s
Dim lKeyCode
On Error Resume Next
lKeyCode = window.event.keyCode
txtKeyCodeKeyDown.value = lKeyCode
txtHexKeyCodeKeyDown.value = "&H" & Hex(lKeyCode)
txtCtrlKeyDown.value = window.event.ctrlKey
txtAltKeyDown.value = window.event.altKey
txtShiftKeyDown.value = window.event.shiftKey
s = LCase(window.event.srcElement.tagName)
txtTagNameKeyDown.value = s
s = window.event.srcElement.id
If Not Err.Number = 0 Then
'Element has no ID
Err.Clear
s = ""
End If
txtIDKeyDown.value = s
If Not IsKeyPressInsideCalculator() Then
'KeyDown event did not originate from inside of divCalculator DIV, exit handler
Exit Function
End If
Select Case lKeyCode
Case Else
'Do nothing
End Select
End Function
'--------------------------------------------------------
'--------------------------------------------------------
'this function checks whether buttons have been pressed inside divCalculator DIV
'it returns True if that is the case, false otherwise
Function IsKeyPressInsideCalculator()
Dim bInsideCalculator
Dim objElement
Dim sObjID
On Error Resume Next
'Check whether we are getting keypresses from inside of divCalculator DIV
bInsideCalculator = False
sObjID = ""
Set objElement = window.event.srcElement
Do
sObjID = objElement.id
If Not Err.Number = 0 Then
'Element has no ID
Err.Clear
sObjID = ""
End If
'MsgBox objElement.tagName & " " & sObjID
If sObjID = "divCalculator" Then
'We found calculator DIV
bInsideCalculator = True
'Exit loop
Exit Do
Else
'Set objElement to it's parent
Set objElement = objElement.parentElement
'Did we hit the top of hierarchy?
If IsEmpty(objElement) Then
Exit Do
ElseIf objElement Is Nothing Then
Exit Do
ElseIf Not Err.Number = 0 Then
Err.Clear
Exit Do
End If
End If
Loop
'Clean up
Set objElement = Nothing
'Return value
IsKeyPressInsideCalculator = bInsideCalculator
End Function
'--------------------------------------------------------
'--------------------------------------------------------
Sub Window_OnLoad()
txtCurrentValue.focus
LetCurrentValue 0
LetPreviousValue 0
mCurrentOperand = calcOperandNone
mbPeriodPressed = False
mlNumberOfDecimalPlaces = 0
mCurrentState = calcStateTypingFirstNumber
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_0()
cmdDigit0.focus
AddToCurrentValue 0
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_1()
cmdDigit1.focus
AddToCurrentValue 1
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_2()
cmdDigit2.focus
AddToCurrentValue 2
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_3()
cmdDigit3.focus
AddToCurrentValue 3
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_4()
cmdDigit4.focus
AddToCurrentValue 4
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_5()
cmdDigit5.focus
AddToCurrentValue 5
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_6()
cmdDigit6.focus
AddToCurrentValue 6
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_7()
cmdDigit7.focus
AddToCurrentValue 7
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_8()
cmdDigit8.focus
AddToCurrentValue 8
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_9()
cmdDigit9.focus
AddToCurrentValue 9
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_Period()
cmdPeriod.focus
If mbPeriodPressed = False Then
mbPeriodPressed = True
mlNumberOfDecimalPlaces = 1
End If
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_Divide()
cmdDivide.focus
LetCurrentOperand calcOperandDivide
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_Multiply()
cmdMultiply.focus
LetCurrentOperand calcOperandMultiply
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_Subtract()
cmdSubtract.focus
LetCurrentOperand calcOperandSubtract
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_Add()
cmdAdd.focus
LetCurrentOperand calcOperandAdd
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_ChangeSign()
cmdChangeSign.focus
LetCurrentValue GetCurrentValue * (-1)
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_Escape()
cmdClearError.focus
LetCurrentValue 0
mbPeriodPressed = False
mlNumberOfDecimalPlaces = 0
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_Clear()
cmdClear.focus
LetCurrentValue 0
LetPreviousValue 0
mCurrentOperand = calcOperandNone
mbPeriodPressed = False
mlNumberOfDecimalPlaces = 0
mCurrentState = calcStateTypingFirstNumber
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_ClearError()
cmdClearError.focus
LetCurrentValue 0
mbPeriodPressed = False
mlNumberOfDecimalPlaces = 0
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub Press_Equal()
cmdEqual.focus
PerformCalculations
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Function LetCurrentValue(currValue)
mcurrCurrentValue = currValue
txtCurrentValue.value = CStr(mcurrCurrentValue)
End Function
'--------------------------------------------------------
'--------------------------------------------------------
Function GetCurrentValue()
GetCurrentValue = mcurrCurrentValue
End Function
'--------------------------------------------------------
'--------------------------------------------------------
Function LetPreviousValue(currValue)
mcurrPreviousValue = currValue
End Function
'--------------------------------------------------------
'--------------------------------------------------------
Function GetPreviousValue()
GetPreviousValue = mcurrPreviousValue
End Function
'--------------------------------------------------------
'--------------------------------------------------------
Function LetCurrentOperand(Operand)
If mCurrentState = calcStateTypingFirstNumber Or mCurrentState = calcStatePerformedOperation Then
'Now reshuffle values in memory
LetPreviousValue GetCurrentValue
LetCurrentValue 0
mbPeriodPressed = False
mlNumberOfDecimalPlaces = 0
mCurrentState = calcStateTypingOperand
ElseIf mCurrentState = calcStateTypingOperand Then
'Do nothing, just accept change in operand
ElseIf mCurrentState = calcStateTypingSecondNumber Then
'Perform calculations first, then proceed with operand change
PerformCalculations
'Now calculated number is first value, state is choosing what to do with it
mCurrentState = calcStateTypingOperand
End If
mCurrentOperand = Operand
End Function
'--------------------------------------------------------
'--------------------------------------------------------
Function GetCurrentOperand()
GetCurrentOperand = mCurrentOperand
End Function
'--------------------------------------------------------
'--------------------------------------------------------
Sub AddToCurrentValue(iDigit)
'msgbox iDigit
If mCurrentState = calcStatePerformedOperation Then
'Reset display
LetCurrentValue 0
ElseIf mCurrentState = calcStateTypingOperand Then
'move to typing the second number
mCurrentState = calcStateTypingSecondNumber
End If
If mbPeriodPressed Then
'working on decimal part of number
If GetCurrentValue => 0 Then
LetCurrentValue GetCurrentValue + iDigit / (10 ^ mlNumberOfDecimalPlaces)
Else
LetCurrentValue GetCurrentValue - iDigit / (10 ^ mlNumberOfDecimalPlaces)
End If
mlNumberOfDecimalPlaces = mlNumberOfDecimalPlaces + 1
Else
'working on whole part of the number
If GetCurrentValue => 0 Then
LetCurrentValue GetCurrentValue * 10 + iDigit
Else
LetCurrentValue GetCurrentValue * 10 - iDigit
End If
End If
End Sub
'--------------------------------------------------------
'--------------------------------------------------------
Sub PerformCalculations()
On Error Resume Next
Select Case mCurrentOperand
Case calcOperandMultiply
LetCurrentValue GetPreviousValue * GetCurrentValue
Case calcOperandDivide
LetCurrentValue GetPreviousValue / GetCurrentValue
Case calcOperandSubtract
LetCurrentValue GetPreviousValue - GetCurrentValue
Case calcOperandAdd
LetCurrentValue GetPreviousValue + GetCurrentValue
Case Else
'Do nothing
'MsgBox "Huh?", vbInformation, "Calculator"
End Select
If Not Err.Number = 0 Then
'Error during calculations
MsgBox "Error during calculations: " & Err.Number & " (" & Err.Description & ")", vbInformation, "Calculator"
Err.Clear
LetCurrentValue 0
LetPreviousValue 0
End If
'Reset operand
mCurrentOperand = calcOperandNone
'Reset decimal place variables
mbPeriodPressed = False
mlNumberOfDecimalPlaces = 0
'Reset calculator state
mCurrentState = calcStatePerformedOperation
End Sub
'--------------------------------------------------------
</SCRIPT>
<BODY ONKEYPRESS="ProcessKeyPress()" ONKEYUP="ProcessKeyUp()" ONKEYDOWN="ProcessKeyDown()">
<!--TOOLBAR_START-->
<!--TOOLBAR_EXEMPT-->
<!--TOOLBAR_END-->
Example 3 - Calculator<BR>
(View source for code)
<P>
<DIV ID="divCalculator" NAME="divCalculator" STYLE="visibility:visible;">
<TABLE BORDER="1" TABINDEX="10" ID="tblCalculator" ACCESSKEY="t">
<THEAD>
<TR>
<TH COLSPAN="4">
<LABEL FOR="txtCurrentValue">C<U>a</U>lculator</LABEL>
</TH>
</TR>
</THEAD>
<TBODY>
<TR>
<TD COLSPAN="4">
<INPUT TYPE="text" ID="txtCurrentValue" NAME="txtCurrentValue" ACCESSKEY="a" TITLE="Calculations window" DIR="LTR">
</TD>
</TR>
<TR>
<TD>
<INPUT TYPE="BUTTON" ID="cmdDigit7" NAME="cmdDigit7" VALUE=" 7 " CLASS="clsDigit" ACCESSKEY="7" TITLE="7" ONCLICK="Press_7()">
</TD>
<TD>
<INPUT TYPE="BUTTON" ID="cmdDigit8" NAME="cmdDigit8" VALUE=" 8 " CLASS="clsDigit" ACCESSKEY="8" TITLE="8" ONCLICK="Press_8()">
</TD>
<TD>
<INPUT TYPE="BUTTON" ID="cmdDigit9" NAME="cmdDigit9" VALUE=" 9 " CLASS="clsDigit" ACCESSKEY="9" TITLE="9" ONCLICK="Press_9()">
</TD>
<TD>
<INPUT TYPE="BUTTON" ID="cmdDivide" NAME="cmdDivide" VALUE=" / " CLASS="clsOperand" ACCESSKEY="/" TITLE="Divide by" ONCLICK="Press_Divide">
</TD>
</TR>
<TR>
<TD>
<INPUT TYPE="BUTTON" ID="cmdDigit4" NAME="cmdDigit4" VALUE=" 4 " CLASS="clsDigit" ACCESSKEY="4" TITLE="4" ONCLICK="Press_4()">
</TD>
<TD>
<INPUT TYPE="BUTTON" ID="cmdDigit5" NAME="cmdDigit5" VALUE=" 5 " CLASS="clsDigit" ACCESSKEY="5" TITLE="5" ONCLICK="Press_5()">
</TD>
<TD>
<INPUT TYPE="BUTTON" ID="cmdDigit6" NAME="cmdDigit6" VALUE=" 6 " CLASS="clsDigit" ACCESSKEY="6" TITLE="6" ONCLICK="Press_6()">
</TD>
<TD>
<INPUT TYPE="BUTTON" ID="cmdMultiply" NAME="cmdMultiply" VALUE=" * " CLASS="clsOperand" ACCESSKEY="*" TITLE="Multiply by" ONCLICK="Press_Multiply">
</TD>
</TR>
<TR>
<TD>
<INPUT TYPE="BUTTON" ID="cmdDigit1" NAME="cmdDigit1" VALUE=" 1 " CLASS="clsDigit" ACCESSKEY="1" TITLE="1" ONCLICK="Press_1()">
</TD>
<TD>
<INPUT TYPE="BUTTON" ID="cmdDigit2" NAME="cmdDigit2" VALUE=" 2 " CLASS="clsDigit" ACCESSKEY="2" TITLE="2" ONCLICK="Press_2()">
</TD>
<TD>
<INPUT TYPE="BUTTON" ID="cmdDigit3" NAME="cmdDigit" VALUE=" 3 " CLASS="clsDigit" ACCESSKEY="3" TITLE="3" ONCLICK="Press_3()">
</TD>
<TD>
<INPUT TYPE="BUTTON" ID="cmdSubtract" NAME="cmdSubtract" VALUE=" - " CLASS="clsOperand" ACCESSKEY="-" TITLE="Subtract" ONCLICK="Press_Subtract">
</TD>
</TR>
<TR>
<TD>
<INPUT TYPE="BUTTON" ID="cmdDigit0" NAME="cmdDigit0" VALUE=" 0 " CLASS="clsDigit" ACCESSKEY="0" TITLE="0" ONCLICK="Press_0()">
</TD>
<TD>
<INPUT TYPE="BUTTON" ID="cmdPeriod" NAME="cmdPeriod" VALUE=" . " CLASS="clsDigit" ACCESSKEY="." TITLE="Decimal period" ONCLICK="Press_Period()">
</TD>
<TD>
<INPUT TYPE="BUTTON" ID="cmdChangeSign" NAME="cmdChangeSign" VALUE="+/-" CLASS="clsOperand" ACCESSKEY="" TITLE="Change sign (F9)" ONCLICK="Press_ChangeSign()">
</TD>
<TD>
<INPUT TYPE="BUTTON" ID="cmdAdd" NAME="cmdAdd" VALUE=" + " CLASS="clsOperand" ACCESSKEY="+" TITLE="Add" ONCLICK="Press_Add">
</TD>
</TR>
<TR>
<TD>
<INPUT TYPE="BUTTON" ID="cmdClear" NAME="cmdClear" VALUE=" C " CLASS="clsClear" ACCESSKEY="a" TITLE="Clear all calculations" ONCLICK="Press_Clear()">
</TD>
<TD>
<INPUT TYPE="BUTTON" ID="cmdClearError" NAME="cmdClearError" VALUE=" E " CLASS="clsClear" ACCESSKEY="Es" TITLE="Clear number on the screen, leaving operand in memory" ONCLICK="Press_ClearError()">
</TD>
<TD COLSPAN="2">
<INPUT TYPE="BUTTON" ID="cmdEqual" NAME="cmdEqual" VALUE=" = " CLASS="clsOperand" ACCESSKEY="=" TITLE="Equals" ONCLICK="Press_Equal()">
</TD>
</TR>
</TBODY>
</TABLE>
</DIV>
<P>
KeyPress, KeyDown and KeyUp event return values:
<TABLE BORDER="1">
<TR>
<TD>Event</TD>
<TD>KeyCode</TD>
<TD>Hex KeyCode</TD>
<TD>Ctrl</TD>
<TD>Alt</TD>
<TD>Shift</TD>
<TD>Element TagName</TD>
<TD>Element ID</TD>
</TR>
<TR>
<TD>KeyPress</TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtKeyCodeKeyPress"></TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtHexKeyCodeKeyPress"></TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtCtrlKeyPress"></TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtAltKeyPress"></TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtShiftKeyPress"></TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtTagNameKeyPress"></TD>
<TD><INPUT TYPE="text" SIZE="20" ID="txtIDKeyPress"></TD>
</TR>
<TR>
<TD>KeyUp</TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtKeyCodeKeyUp"></TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtHexKeyCodeKeyUp"></TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtCtrlKeyUp"></TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtAltKeyUp"></TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtShiftKeyUp"></TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtTagNameKeyUp"></TD>
<TD><INPUT TYPE="text" SIZE="20" ID="txtIDKeyUp"></TD>
</TR>
<TR>
<TD>KeyDown</TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtKeyCodeKeyDown"></TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtHexKeyCodeKeyDown"></TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtCtrlKeyDown"></TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtAltKeyDown"></TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtShiftKeyDown"></TD>
<TD><INPUT TYPE="text" SIZE="4" ID="txtTagNameKeyDown"></TD>
<TD><INPUT TYPE="text" SIZE="20" ID="txtIDKeyDown"></TD>
</TR>
</TABLE>
</BODY>
</HTML>