Press enter to skip the top menu

Web Accessibility for the Impaired

Forms

Introduction

Our objective here is to create a form that a visually impaired person could use with the aid of a screen reader. For this we need to compliment all of the visual cues that a sighted user can use with textual cues that the screen reader can call out to the visually impaired user.

Up to now we have used one set of HTML elements to build our web page. They are the elements that were defined on the page Layout and have been used in all the exercises up to now. Here we are introducing a new set of HTML elements that that are specifically designed to mark up forms. Those elements are:

As our practice has been up until now, we ensure that all of the HTML features are used as they are intended to be used. This includes the following:

Go to top

Form Layout

In Fig 1 we have a completed form. Before examining the HTML and CSS code that has built and styled it, we shall first examine the completed form and the functionality of each element therein.

Looking down the left hand column we can deuce from the labels that this is a form for calculating a very simple payroll application

At the very top is a notice informing users that an asterix on an element means that that particular element has to be filled in. From this we can deduce that the employee's name, their hourly rate and the number of hours worked has to be entered by the user.

Fig 1: the form before data entry starts

From the down-pointing arrow on the element that whill hold the employee's name we can deduce that this is a drop down list that contains the names of the employees. This one of the features that makes the form compliant with the heuristic Error prevention - have the user pick items from the list rather than typing them in.

For the values of the hourly rate and the hours worked we have prompts inside them specifying that the rate must be a number in the range 20 - 50 and the hours in the range 5 - 60. This complies with the heuristic Help and Documentation since it informs the user what range of values the numbers must lie within.

The elements with labels for gross, tax and net have no values to display. This is because these will display the results of the payroll calculation. Also the form user can't access them as they are disabled; only the program that calculates the payroll can do that.

The next two items we meet are buttons. Their labels should indicate their functions.

Finally we have the currently blank element at the bottom. This will show how correct or otherwise the user input was.

Go to top

HTML Code

Listing 1 below contains the HTML code for the form. The code can be broken into sections as follows

Lisitnig 1

                    <form id="Payroll">
                        <legend>All fields marked with * are required</legend>
                        <fieldset>
                            <label for="EmployeeName"  class="first" style="height: 23px;"><b><u>N</u></b>ame *</label>
                            <select id="EmployeeName" name="EmployeeName" accesskey="N" minlength="1">
                                <option value="">--please choose a name--</option>
                                <option value="John Smith">John Smith</option>
                                <option value="Tom Thumb">Tom Thumb</option>
                                <option value="Dick Jones">Dick Jones</option>
                                <option value="Harry Potter">Harry Potter</option>
                            </select>
                            <label for="HourlyRate" >Hourly <b><u>R</u></b>ate *</label>
                            <input type="number" id="HourlyRate" name="HourlyRate" placeholder="Rate: between 20 and 50" min="20" max="50" required accesskey="R">
                            <label for ="HoursWorked">Hours <b><u>W</u></b>orked *</label>
                            <input type="number" id="HoursWorked" name="HoursWorked" placeholder="Hours: between 5 and 60" min="5" max="60" required accesskey="W" >
                            <label for="GrossPay">Gross Income</label>
                            <input type="text" id="GrossPay" name="GrossPay" disabled>
                            <label for="TaxPaid">Tax paid</label>
                            <input type="text" id="TaxPaid" name="TaxPaid" disabled>
                            <label for="NetPay">Net Income</label>
                            <input type="text" id="NetPay" name="NetPay" disabled>
                            <button type="button" name="clearFields" accesskey="F" onclick="clearForm()">
                                Clear <b><u>F</u></b>ields
                            </button>
                            <button type="button" name="Calculate" accesskey="C" onclick="calculatePay()">
                                <b><u>C</u></b>alculate
                            </button>
                        </fieldset>
                        <section>
                            <textarea id="Feedback"></textarea>
                        </section>
                    </form>
                

Go to top

Above is the contents of line 4 of Listing 1. It is reproduced here for clarity. The entire line defines a label element.

The for attribute has a value of "EmployeeName". This means that this label is linked to an element whose id value is "EmployeeName".

The somewhat cryptic <b><u>N</u></b> can be explained as follows:

  • The initial <b><u> are two tags signifying bold and underline. This means that the next text item will be bold and underlined.
  • The next piece of text is the letter N, which if you check Fig 1 you will see that it is in bold
  • The final part, </u></b> are simply the closing bold and underline tags.

The HTML select element is the equivalent of the drop down list in other GUI applications. It gives the user a number of options to choose from. The options are normally hidden until the user selects the element. When this occurs the list of options appear. This is shown in Fig 2 below.

The first attribute we look at is id In this case the value of the attribute is "EmployeeName". This links our select element to the label element above since the for attribute of that element has the same value.

The name attribute is necessary only if we are connecting to a server. In this case we are not, and so this attribute in not necessary in this instance.

The accesskey attribute is set to "N". The purpose of this is to make the select element operable from the keyboard. In Windows this is achieved by the user pressing the N while holding down the Alt key. In the Mac it is achieved by pressing N while holding down the Control and Option keys.

The final attribute is minlength. Since the options on the drop down list will be names and surnames, and therefore text, we are specifying that the value of the person's name cannot be blank.

The select element contains within it a number of option elements. Each of those elements represents a choice on the drop down list.

Above we have the code for the first option element, which is a reproduction of line 6 of listing 1.

The only attribute for the option element is value. In this case it is set to null.

This particular option acts as a prompt for the user as shown in the text '--please choose a name--'.

This is the first of the option elements that we can get actual values from. At first glance, however it looks confusing at first glance.

The cause of the confusion is that the phrase "John Smith" occurs twice.

The first occurrence of this phrase is when it assigned as the value of the option element. In this case if the user selects this choice then "John Smith" will also become the value of the select element as well.

The second occurrence is between the opening and closing tags of the option element. In this case it acts as a prompt for the user as to which of the options in the drop down box they are to select.

Fig 2: Dropdown list revealing its contents

Go to top

The <input> elements

The above code snippet is line 13 in Listing 1.

The input elements work differently from the select elements and so we shall spend some time discussing them.

In the snipped above the first attribute of input we meet is type. Here it is given a value of "number". This means that only numeric values will be allowed. Thus entering text will put the element into error mode.

We have already met the next two attributes: id and name. They behave here exactly as they do in the select element and so there is no need to discuss them further.

The attribute "placeholder" has a value of "Rate: between 20 and 50". When the form is loaded this text appears in the input element. It is simply used as a prompt to the user what they are to enter into the field. Thus the user knows that they must enter a valie between 20 and 50. This complies with the heuristic help and documentation as we are supplying the user with help to complete the form accurately.

The attributes min and max specify the smallest and largest values allowed for the hourly rate. Again this complements the help supplied by the placeholder. In this case, however, the data in the field will be invalid. This can be picked up later by the JavaScript code in order to decide whether to proceed with the payroll processing or not. This is further enhancing the error prevention capabilities of the form.

The attribute required is given no value. The fact that it is there specifies that the field can't be left empty. This further enhances the error prevention capabilities of the form.

The final attribute here is accesskey which has a value of "R". This means that those who cannot use the mouse can still access the field using the keyboard. They can use alt + R on Windows or Control + Option + R on the Mac. This enhances the usability of the form.


Go to top

Buttons

Listing 1-1
                        <button type="button" name="clearFields" accesskey="F"> onclick = "clearForm()">
                            Clear <b><u>F</u></b>ields>
                        </button>>
                    

Above we are showing lines 22 - 24 of Listing 1. The only new item here is the onclick. This is an event occurs when the user clicks the left mouse button. Usually an event causes a program function to run. In this case the function is clearForm().

The text between the opening and closing button tags is "Clear Fields"

The button element spanning lines 25 - 27 works in exactly the same way as the "clearFields" button.


Go to top

Textarea

Fig 3: Correct data in all input fields
Listing 1-2
                    <section>
                            <textarea id="Feedback"></textarea>
                        </section>
                        

The above code snipped is lines 29 - 31. The element textarea has an appropriate id value, "Feedback" as in this case it gives us feedback on the data entered by the user.

This is a good example of the heuristic Visibility of system status. It tells us that the employee's name has been provided and that the hours and rate are within the correct range, as shown in Fig 3.

Go to top

CSS - Highlighting invalid data

In Fig 1 and Fig 2 all of the fields on the form have thin black borders. In Fig 3, however, both the hours and the rate fields have thick green borders. Why is this?

The reason is that we want to enhance the Visibility of System Staus heuristic further. When a user enters data into the field, giving it a green border indicates that the data entered is valid. By giving it a red border we indicate that the data is invalid, in other words, in error.

How do we achiece this effect? With CSS!

Recall that when we were defining our input elements that we gave them a minimum and a maximum values using the attributes min and max.

If a user enters a value that is within the constrints specified by the min and max attributes then that element is in a valid state.

On the other hand if the user's entry is outside of those constraints then the input element is in an invalid state.

We can use those pseudoclasses to style the elements. The CSS code snippet below shows us how.

Listing 1-3
                input[type="number"]:invalid {
                    border: thick solid darkred;
                }
                input[type="number"]:valid {
                    border: medium solid darkgreen;
                }
                input[type="number"]:placeholder-shown {
                    border: thin solid black;
                }
                

The CSS code above targets the input elements whose type value is number. We have two of those in our form: where we enter data for the hourly rate and the hours worked. We have specified that the permitted range for rate is 20 - 50 and for hours is 5 - 60.

Data within these ranges will cause the elements to be in a valid state while data outside the ranges will cause them to be in an invalid state.

The first CSS item above states that if the state of the element is invalid then the border should thick and dark red.

The second item states that if the state of the element is valid the border should be green.

These colours comply with the second usability heuristic Match Between the System and the Real World since the colour red indicates that something is wrong while green indicate that things are ok. Typical example is traffic lights.

Also using colours to visually indicate a state of valid/invalid is complying with the first heuristic: Visibility of System Status.

The third item item states that if the placeholder of an input element is showing then the border should be a thin black line.

Fig 4: too high a value for the rate

Fig 1 shows the form newly loaded. No data has been entered yet and the placeholders are showing for the hours and the rate. The result of placeholders showing is that the input elements have a thin black border.

Fig 2 shows the drop down list. As yet no data has been entered.

Fig 3 shows the form after valid data has been entered and the "Calculate" button has been pressed. At the top the employee's name is showing. The input elements for the hours and the rate have a greed border around them indicating that the data is valid. The gross, tax and net are shown in the appropriate elements.

Finally the textarea element at the bottom of the form verifies that:

This again another example of Visibility of System Status being applied in the form. We are assured that the system has tested all of our input and used it to calculate the output, i.e. the values of the gross, tax and net.

Fig 4 shows the form after an invalid data was provided for the hourly rate. This is indicated by the rate input element having a thich red border while the hours element has a greed border. Although the user tried to calculate the payroll it was not successful as shown by the contents of the textarea. The third line tells us that the rate is out of range, which is why no calculation has taken place.

Fig 5: At the bottom the user is informed of their errors

Finally Fig 5 shows us another erroneous situation; the numeric data is correct but no employee has been selected. Again no calculation has taken place and the first line in the text area tells us that an employee name was not supplied.

Go to top

JavaScript

Introduction

Below in Listing 2 is the JavaScript code that we are using to:

  • retrieve the validity of the HoursWorked and HourlyRate elements and the value of the EmployeeName element - lines 2 - 4
  • from those values create a a series of statements that will inform the user of the validity of the data
  • If all of the entered data is valid then calculate the values of the gross, tax and net and display them.

We shall now look at how these goals are acheived.

Listing 2
                        function calculatePay(){
                            var hoursTrue=document.getElementById("HoursWorked").checkValidity();
                            var rateTrue=document.getElementById("HourlyRate").checkValidity();
                            var nameValue=document.getElementById("EmployeeName").value;
                            if (nameValue.length > 0)
                                var NameState= "A name has been provided"
                            else
                                var NameState ="No name has been provided"
                            if (hoursTrue == true) 
                                var HoursState = "Hours is in the correct range";
                            else
                                var HoursState = "Hours is outside the correct range"
                            if (rateTrue == true) 
                                var RateState = "Rate is in the correct range";
                            else
                                var RateState = "Rate is outside the correct range";
                            var SysMessage=NameState + "\n" + HoursState + "\n" + RateState;
                            clearOutput();
                            document.getElementById("Feedback").value=SysMessage;
                            if ((hoursTrue == true && rateTrue == true) && nameValue.length > 0){
                                let sngHours=document.getElementById("HoursWorked").value;
                                let curRate=document.getElementById("HourlyRate").value;
                                let curGross=sngHours*curRate;
                                let curTax=curGross * 0.25;
                                let curNet=curGross - curTax;
                                document.getElementById("GrossPay").value=curGross;
                                document.getElementById("TaxPaid").value=curTax;
                                document.getElementById("NetPay").value=curNet;
                            }
                        }
                        function clearOutput(){
                            document.getElementById("GrossPay").value=null;
                            document.getElementById("TaxPaid").value=null;
                            document.getElementById("NetPay").value=null;
                            document.getElementById("Feedback").value=null;
                        }
                        function clearForm(){
                           let myForm = document.getElementById("Payroll");
                           myForm.reset();
                        }
                    

The code in Listing 2 above is too long to explain in a single unit and so it is broken up below into its logical subsections. Each subsection will be explained separately.


Go to top

Error checking the data

Listing 2-1
                            var hoursTrue=document.getElementById("HoursWorked").checkValidity();
                            var rateTrue=document.getElementById("HourlyRate").checkValidity();
                            var nameValue=document.getElementById("EmployeeName").value;
                    

Here we have three variables: hoursTrue, rateTrue and nameValue

Lines 1 and 2 read the validity of the input elements HoursWorked and HourlyRate and store the data in the variables hoursTrue and rateTrue.

Thus if the value of HoursWorked is 30 then hoursTrue will be true. On the othe hand if HoursWorked has a value of 2, then hoursTrue will have a value of false.

The same applies to line 3.

We test the select element EmployeeName differently. Instead of checking its validity we read its actual value. This will be a blank if the user has not selected any of the option in the drop down list or a piece of text if they have. The result is stored in the variable nameValue.


Go to top

Composing the user feedback

Listing 2-2
                            if (nameValue.length > 0)
                                var NameState= "A name has been provided"
                            else
                                var NameState ="No name has been provided"
                            if (hoursTrue == true) 
                                var HoursState = "Hours is in the correct range";
                            else
                                var HoursState = "Hours is outside the correct range"
                            if (rateTrue == true) 
                                var RateState = "Rate is in the correct range";
                            else
                                var RateState = "Rate is outside the correct range";
                    

Now that we have information about the data that the user has entered we shall now look at how that can be transformed into jargon-free information about the status of the system.

Listing 2-2 consists of three if...else constructs, each spanning lines 5 - 8, 9 - 12 and 13 - 16.

Line 5 tests if the length of the string stored in the variable NameValue is greater than zero. If it is then line 6 is executed and the text "A name has been provided" is stored in the variable NameState.

If it is not greater than zero then line 6 is skipped and control jumps to line 8 and the text "No name has been provided" is stored in NameState.

The other two if...else constructs behave in the same way.


Go to top

Delivering the feedback

Go to top
Listing 2-3
                                clearOutput();
                                var SysMessage=NameState + "\n" + HoursState + "\n" + RateState;
                                document.getElementById("Feedback").value=SysMessage;
                    

In this subsection we provide the user with the information that we gathered and analysed in Listing 2-1 and Listing 2-2

At line 17 is a call to the function clearOutput(). We shall look at this later.

At line 18 the variables NameState, HoursState and RateState are appended to the variable SysMessage and at line 19 the value of SysMessage gets displayed in the textarea element Feedback.

You can see the result of this in Fig 3, Fig 4 and Fig 5.


Go to top

Processing the validated data

Listing 2-4
                            if ((hoursTrue == true && rateTrue == true) && nameValue.length > 0){
                                let sngHours=document.getElementById("HoursWorked").value;
                                let curRate=document.getElementById("HourlyRate").value;
                                let curGross=sngHours*curRate;
                                let curTax=curGross * 0.25;
                                let curNet=curGross - curTax;
                                document.getElementById("GrossPay").value=curGross;
                                document.getElementById("TaxPaid").value=curTax;
                                document.getElementById("NetPay").value=curNet;
                            }
                        }
                    

Given that all of the user input has now been tested and the user themselves informed of the validity of the input we are now in the position of either processing the same input or else reject it.

At line 20 we test that the variables hoursTrue and rateTrue have a value of true and that the variable nameValue has a length greater that zero. If this is true then the code spanning lines 21 - 28 will be executed.

These eight lines can be divided into three parts: input, process and output.

The input section is where the program gets input for external sources; in our case that means the three controls on the form where the user enters their data. These are lines 21 and 22

The processing section spans lines 23 - 25 where the values f the gross, tax and net are calculated.

Finally the output section spans lines 26 - 28. This is where the calculated values from the processing section are written to the form.


Go to top

Clearing the output area

Next we come to the function clearOutput() in Listing 2-5. This simply sets the values of the fields containing the gross, tax and net to blank. It also sets the textarea element to blank.

Listing 2-5

                        function clearOutput(){
                            document.getElementById("GrossPay").value=null;
                            document.getElementById("TaxPaid").value=null;
                            document.getElementById("NetPay").value=null;
                            document.getElementById("Feedback").value=null;
                        }

                    

Resetting the form

Finally we come to the function clearForm() at Listing 2-6. At line 39 it uses the form's reset() method to clear all data from the form. It is equivalent to reloading the form.

Listing 2-6

                        function clearForm(){
                           let myForm = document.getElementById("Payroll");
                           myForm.reset();
                        }
                    
Go to top