diff --git a/techniques/aria/ARIA18.html b/techniques/aria/ARIA18.html index ff6f6d5e66..8443c7496f 100644 --- a/techniques/aria/ARIA18.html +++ b/techniques/aria/ARIA18.html @@ -1,56 +1,72 @@ -Using aria-alertdialog to Identify Errors

Using aria-alertdialog to Identify Errors

ID: ARIA18

Technology: aria

Type: Technique

When to Use

-

Technologies that support Accessible Rich Internet Applications (WAI-ARIA).

-

Description

-

The purpose of this technique is to alert people that an input error has occurred. Using role="alertdialog" creates a notification. This notification should be modal with the following characteristics: -

- - -

Note that the alertdialog should not be present in a way that it will be accessed by assistive technology until it is needed. One way to do this is not to include it in the static HTML and instead to insert it into the DOM via script when the error condition is triggered. The insertion would correspond to the following HTML sample. -

-

Examples

-
-

Alert dialog

- -

This example shows how a notification using role="alertdialog" can be used to notify someone they have entered invalid information.

-
<div role="alertdialog" aria-labelledby="alertHeading">
-  <h1 id="alertHeading">Error</h1>
-  <p>Employee's Birth Date is after their hire date.
-   Please verify the birth date and hire date.</p>
-  <button>Save and Continue</button>
-  <button>Return to page and correct error</button>
+
+
+
+	Using aria-alertdialog to Identify Errors
+
+
+	

Using aria-alertdialog to Identify Errors

+
+

ID: ARIA18

+

Technology: aria

+

Type: Technique

+
+
+

When to Use

+

Technologies that support Accessible Rich Internet Applications (WAI-ARIA).

+
+
+

Description

+

The purpose of this technique is to alert people that an input error has occurred. Using role="alertdialog" creates a notification. This notification should be modal with the following characteristics:

+
    +
  • aria-label or aria-labelledby attribute gives the alertdialog an accessible name.
  • +
  • The alertdialog contains at least one focusable element, and the focus should move to that element when the alertdialog opens.
  • +
  • The tab order is constrained within the alertdialog whilst it is open.
  • +
  • When the alertdialog is dismissed, the focus moves back to the position it had before the alertdialog opened, if possible.
  • +
+
+
+

Examples

+
+

Alert dialog

+

This example shows how a notification using role="alertdialog" can be used to notify someone they have entered invalid information.

+
<div aria-modal="true" role="alertdialog" aria-labelledby="alert-heading">
+  <h1 id="alert-heading">Error</h1>
+  <p>Employee's Birth Date is after their hire date. Please verify the birth date and hire date.</p>
+  <button type="button">Return to page and correct error</button>
 </div>
-

Working example: Alert dialog.

- -
-

Tests

-

Procedure

-
    -
  1. Trigger the error that causes the alertdialog to appear.
  2. -
  3. Determine that the alertdialog contains at least one focusable element, and the focus moves to that element when the alertdialog opens.
  4. -
  5. Determine that the tab order is constrained within the alertdialog while it is open, and when the alertdialog is dismissed, the focus moves back to the position it had before the alertdialog opened, if possible.
  6. -
  7. Examine the element with alertdialog applied.
  8. -
  9. Determine that either the aria-label or aria-labelledby attribute has been correctly used to give the alertdialog an accessible name.
  10. -
  11. Determine that the contents of the alertdialog identifies the input error.
  12. -
  13. Determine whether contents of the alertdialog suggests how to fix the error.
  14. -
-
-

Expected Results

-
    -
  • Checks #2, #3, #5 and #6 are true. For Success Criterion 3.3.3, check #7 is also true.
  • -
-
-

Resources

- - - -
- \ No newline at end of file +

Working example: Using aria-alertdialog to Identify Errors +.

+
+
+
+

Tests

+
+

Procedure

+
    +
  1. Trigger the error that causes the alertdialog to appear.
  2. +
  3. Determine that the alertdialog contains at least one focusable element, and the focus moves to that element when the alertdialog opens.
  4. +
  5. Determine that the tab order is constrained within the alertdialog while it is open, and when the alertdialog is dismissed, the focus moves back to the position it had before the alertdialog opened, if possible.
  6. +
  7. Examine the element with alertdialog applied.
  8. +
  9. Determine that either the aria-label or aria-labelledby attribute has been correctly used to give the alertdialog an accessible name.
  10. +
  11. Determine that the contents of the alertdialog identifies the input error.
  12. +
  13. Determine whether contents of the alertdialog suggests how to fix the error.
  14. +
+
+
+

Expected Results

+
    +
  • Checks #2, #3, #5 and #6 are true. For Success Criterion 3.3.3, check #7 is also true.
  • +
+
+
+ +
+

Resources

+ +
+ + \ No newline at end of file diff --git a/techniques/aria/ARIA19.html b/techniques/aria/ARIA19.html index aec3363950..bc4429521a 100644 --- a/techniques/aria/ARIA19.html +++ b/techniques/aria/ARIA19.html @@ -1,4 +1,6 @@ -Using ARIA role=alert or Live Regions to Identify Errors

Using ARIA role=alert or Live Regions to Identify Errors

ID: ARIA19

Technology: aria

Type: Technique

When to Use

+Using ARIA role=alert or Live Regions to Identify Errors +

Using ARIA role=alert or Live Regions to Identify Errors

+

When to Use

Technologies that support Accessible Rich Internet Applications (WAI-ARIA).

Description

The purpose of this technique is to notify Assistive Technologies (AT) when an input error occurs. The aria-live attribute makes it possible for an AT (such as a screen reader) to be notified when error messages are injected into a Live Region container. The content within the aria-live region is automatically read by the AT, without the AT having to focus on the place where the text is displayed. @@ -8,40 +10,70 @@

Injecting error messages into a container with role=alert already present in the DOM

-

The following example uses role=alert which is equivalent to using aria-live=assertive. -

-

In the example there is an empty error message container element with aria-atomic=true and an aria-live property or alert role present in the DOM on page load. The error container must be present in the DOM on page load for the error message to be spoken by most screen readers. aria-atomic=true is necessary to make Voiceover on iOS read the error messages after more than one invalid submission. -

-

jQuery is used to test if the inputs are empty on submit and inject error messages into the live region containers if so. Each time a new submit is attempted the previous error messages are removed from the container and new error messages injected. -

-
$(document).ready(function(e) {
-  $('#signup').submit(function() {
-    $('#errors').html('');
-    if ($('#first').val() === '') {
-      $('#errors').append('<p>Please enter your first name.</p>');
-    }
-    if ($('#last').val() === '') {
-      $('#errors').append('<p>Please enter your last name.</p>');
-    } 
-    if ($('#email').val() === '') {
-      $('#errors').append('<p>Please enter your email address.</p>');
-    }
-    return false;
-  });
+            

The following example uses role=alert which is equivalent to using aria-live=assertive.

+

In the example there is an empty error message container element with aria-atomic=true and an aria-live property or alert role present in the DOM on page load. The error container must be present in the DOM on page load for the error message to be spoken by most screen readers. aria-atomic=true is necessary to make Voiceover on iOS read the error messages after more than one invalid submission.

+

JavaScript is used to test if the inputs are empty on submit and inject error messages into the live region containers if so. Each time a new submit is attempted the previous error messages are removed from the container and new error messages injected.

+ +
// Get the form element
+const form = document.getElementById('signup');
+
+// Listen for when the form is submitted
+form.addEventListener('submit', function(event) {
+
+// Stop the form from actually submitting (so we can check it first)
+event.preventDefault();
+
+// Get references to the input fields
+const firstNameInput = document.getElementById('first');
+const lastNameInput = document.getElementById('last');
+const emailInput = document.getElementById('email');
+
+// Get the element where we'll show error messages
+const errorsContainer = document.getElementById('errors');
+
+// Create an empty array to collect error messages
+const errorMessages = [];
+
+// Check if first name is empty
+if (firstNameInput.value === '') {
+  errorMessages.push('<li>Please enter your first name.</li>');
+}
+
+// Check if last name is empty
+if (lastNameInput.value === '') {
+  errorMessages.push('<li>Please enter your last name.</li>');
+}
+
+// Check if email is empty
+if (emailInput.value === '') {
+  errorMessages.push('<li>Please enter your email address.</li>');
+}
+
+// Clear the errors container first
+// (This helps screen readers notice when new content appears)
+errorsContainer.innerHTML = '';
+
+// Wait 500 milliseconds, then show the errors
+setTimeout(function() {
+  // If there are any errors, wrap them in a list and display them
+  if (errorMessages.length > 0) {
+    errorsContainer.innerHTML = '<ul>' + errorMessages.join('') + '</ul>';
+  }
+}, 500);
 });
<form name="signup" id="signup">
   <p id="errors" role="alert" aria-atomic="true"></p>
   <div>
-    <label for="first">First Name (required)</label><br>
-    <input type="text" name="first" id="first">
+    <label for="first">First Name (required)</label>
+    <input autocomplete="given-name" type="text" name="first" id="first">
   </div>
   <div>
-    <label for="last">Last Name (required)</label><br>
-    <input type="text" name="last" id="last">
+    <label for="last">Last Name (required)</label>
+    <input autocomplete="family-name" type="text" name="last" id="last">
   </div>
   <div>
-    <label for="email">Email (required)</label><br>
-    <input type="text" name="email" id="email">
+    <label for="email">Email (required)</label>
+    <input autocomplete="email" type="text" name="email" id="email">
   </div>
   <div>
     <input type="submit" name="button" id="button" value="Submit">
@@ -52,9 +84,9 @@ 

Injecting error messages into a container with role=alert already present in

Tests

Procedure

    -
  1. Determine that an empty error container with role=alert or aria-live=assertive attribute is present in the DOM at page load.
  2. -
  3. Trigger the error that causes the content in the live region to appear or update.
  4. -
  5. Determine that the error message was injected into the already present error container.
  6. +
  7. Determine that an empty error container with role=alert or aria-live=assertive attribute is present in the DOM (Document Object Model). at page load.
  8. +
  9. Trigger the error that causes the content in the live region to appear or update.
  10. +
  11. Determine that the error message was injected into the already present error container.
@@ -73,9 +105,6 @@

Resources

  • WAI-ARIA Authoring Practices Guide
  • -
  • - HTML5 Accessibility Chops: ARIA role=alert browser support -
  • WAI-ARIA, Supported States and Properties, aria-describedby
  • diff --git a/techniques/aria/ARIA21.html b/techniques/aria/ARIA21.html index 894b0a649a..0a3b120b05 100644 --- a/techniques/aria/ARIA21.html +++ b/techniques/aria/ARIA21.html @@ -1,142 +1,173 @@ -Using aria-invalid to Indicate An Error Field

    Using aria-invalid to Indicate An Error Field

    ID: ARIA21

    Technology: aria

    Type: Technique

    When to Use

    + + + + Using aria-invalid to Indicate An Error Field + + +

    Using aria-invalid to Indicate An Error Field

    +
    +

    ID: ARIA21

    +

    Technology: aria

    +

    Type: Technique

    +
    +
    +

    When to Use

    HTML with Accessible Rich Internet Applications.

    -

    Description

    -

    This technique demonstrates how aria-invalid may be employed to specifically identify fields that have failed validation. Its use is most suitable when:

    -
      -
    • The error description does not programmatically identify the failed fields
    • -
    • The failed fields are identified in a manner that is not available to some users - for example by using an error-icon rendered visually by some technique that does not rely on color such as a visual cue like a border.
    • -
    -

    One of the above two situations may be true for a field which has programmatically associated label and / or instructions that conveys data format, a data range, or the required property.

    -

    While it is always preferable to programmatically associate specific error description with the failed field, the page's design or the framework employed may sometimes constrain the author's ability to do so. In these cases, authors may programmatically set aria-invalid to "true" on the fields that have failed validation. This is interpretable mainly by assistive technologies (like screen readers / screen magnifiers) employed by users who are vision impaired. When a field has aria-invalid set to “true”, VoiceOver in Safari announces invalid data when the field gets focus; JAWS and NVDA notify the error as an invalid entry.

    -

    This ARIA attribute has to be set / turned on programmatically. It should not be set to “true” before input validation is performed or the form is submitted. Setting aria-invalid to "false" is the same as not placing the attribute for the form control at all. Quite understandably, nothing is conveyed by assistive technology to users in this case.

    -

    When visible text is used to programmatically identify a failed field and / or convey how the error can be corrected, setting aria-invalid to "true" is not required from a strict compliance standpoint but may still provide helpful information for users.

    -

    Examples

    -
    -

    Using aria-invalid on required fields

    +
    +
    +

    Description

    -

    The aria-invalid attribute is used on required fields that have no input. A message above the form conveys that form submission has failed due to this.

    -

    A portion of the jQuery code and the HTML form markup follow:

    -
    <script>
    -...
    -...
    -   if ($('#first').val() === '') {
    -      $('#first').attr("aria-invalid", "true");
    -$("label[for='first']").addClass('failed');
    -   }
    -   if ($('#last').val() === '') {
    -      $('#last').attr("aria-invalid", "true");
    -$("label[for='last']").addClass('failed');
    -   }
    -   if ($('#email').val() === '') {
    -      $('#email').attr("aria-invalid", "true");
    -$("label[for='email']").addClass('failed');
    -   }
    -...
    -...
    -</script>
    +			

    This technique demonstrates how aria-invalid may be used to identify fields that have failed validation. The aria-invalid attribute can be used as part of a custom validation pattern instead of using the HTML required attribute coupled with specific input types (for example: type="email").

    + +

    The aria-invalid attribute should not be set to "true" before input validation is performed. Setting aria-invalid to "false" is the same as not placing the attribute for the form control.

    -<style> - label.failed { - border: red thin solid; - } -</style> +
    +
    +

    Examples

    +
    +

    Using aria-invalid on required fields

    +

    The aria-invalid attribute is used on required fields that are empty when the form is submitted. A message above the form tells the user that there are form fields that need to be completed.

    -<form name="signup" id="signup"> +
    HTML:
    +
    <form name="signup" id="signup" method="post" action="">
    +  <p id="errors" aria-live="assertive"></p>
       <div>
         <label for="first">First Name (required)</label>
    -    <input type="text" name="first" id="first">
    +    <input aria-invalid="false" autocomplete="given-name" id="first" name="first" type="text">
       </div>
       <div>
         <label for="last">Last Name (required)</label>
    -    <input type="text" name="last" id="last">
    +    <input aria-invalid="false" autocomplete="family-name" id="last" name="last" type="text">
       </div>
       <div>
         <label for="email">Email (required)</label>
    -    <input type="email" name="email" id="email">
    +    <input aria-invalid="false" autocomplete="email" id="email" name="email" type="text">
       </div>
       <div>
    -    <input type="submit" name="button" id="button" value="Submit">
    +    <button id="button" type="submit">Sign Up</button>
       </div>
     </form>
    -

    Working example: Using aria-invalid on required fields.

    -
    -
    -

    Identifying errors in data format

    +
    CSS:
    +
    label.failed {
    +  background:#CE0000;
    +  color:#fff;
    +}
    -

    aria-invalid and aria-describedby are used together to indicate an error when the personal identification number (PIN), email address, or start date are not in the expected format. The error message is associated with the field using aria-describedby, and aria-invalid makes it easier to programmatically find fields with errors.

    -

    Below is the rendered HTML code for the email address field in Example 1: When an invalid email address is entered by the user such as "samexample.com" (instead of sam@example.com), the HTML code is:

    -
    <div class="control">
    -  <div>
    -    <label for="email">Email address: [*]</label>
    -    <input type="email" name="email" id="email" class="error"
    -     aria-invalid="true" aria-describedby="err_1">
    -  </div>
    -  <span class="errtext" id="err_1">Error: Incorrect data</span>
    -</div>
    -

    And when no data is entered in the email field, the HTML code is:

    +
    JavaScript:
    +

    These are the two JavaScript functions that set the aria-invalid="true" attribute and then create the validation error message:

    +
    const requiredFields = ['first', 'last', 'email'];
    +let errorCount = 0;
    +		
    +requiredFields.forEach(fieldId => {
    +  const field = document.getElementById(fieldId);
    +  if (field.value.trim() === '') {
    +    field.setAttribute('aria-invalid', 'true');
    +    document.querySelector(`label[for='${fieldId}']`).classList.add('failed');
    +    errorCount++;
    +  }
    +});
    +		
    +if (errorCount > 0) {
    +  errText.textContent = `Please complete all ${errorCount} required ${errorCount === 1 ? 'field' : 'fields'} and retry`;
    +}
    +

    Working example: Using aria-invalid on required fields.

    +
    +
    +

    Identifying errors in data format

    +

    In this example, aria-invalid and aria-describedby are used together to indicate an error when the personal identification number (PIN), email address, or start date are not in the expected format. The error message is associated with the field using aria-describedby, and aria-invalid makes it easier to programmatically find fields with errors.

    + +

    Features

    +
      +
    • Adds an aria-invalid="true" attribute on a form field if the entered data is missing or not in the expected format: +
        +
      • The PIN contains fewer than 4 characters;
      • +
      • The email address doesn't meet the required format;
      • +
      • The last name must contain at least two letters;
      • +
      • The policy start date is before today's date.
      • +
      +
    • +
    • Displays an error message which is programmatically connected to the relevant form field using the aria-describedby attribute.
    • +
    +
    HTML:
    <div class="control">
    -  <div><label for="email">Email address: [*]</label>
    -    <input type="email" name="email" id="email" class="error"
    -     aria-invalid="true" aria-describedby="err_2">
    -  </div>
    -  <span class="errtext" id="err_2">Error: Input data missing</span>
    +  <label for="pin4">PIN * (4 digits)</label> 
    +  <input id="pin4" name="pin4" pattern="\d{4}" size="4" type="text" aria-describedby="pin4-errormsg" aria-invalid="true" class="error">
    +  <span class="errtext" id="pin4-errormsg">Error: PIN is required</span>
     </div>
    -

    jQuery code: jQuery is used to add aria-invalid or aria-describedby attributes as well as the class attribute and append the error text. This is the code that inserts aria-invalid and class="error" but does not associate the error text "incorrect data" with the control programmatically:

    - -
    $(errFld).attr("aria-invalid", "true").attr("class", "error");
    -// Suffix error text:
    -$(errFld).parent().append('<span class="errtext">Error: Incorrect data</span>');
    -

    CSS Code:

    -
    input.error {
    -  border: red thin solid;
    +
    CSS:
    +
    .error {
    +  border: #CE0000 thin solid;
     }
    -
    -span.errtext {
    -  background-color: #EEEEFF;
    -  background-image:url('images/iconError.gif');
    -  background-position:right;
    -  background-repeat:no-repeat;
    -  border: red thin solid;
    +	
    +.errtext {
    +  background: #EEEEFF url('error-icon.svg') no-repeat right 0.25em center;
    +  color:#CE0000;
    +  display:block;
       margin-bottom: 1em;
    -  padding: .25em 1.4em .25em .25em;
    +  max-width: fit-content;
    +  padding: .25em 1.6em .25em .25em;
     }
    -

    Working example: Identifying errors in data format.

    -
    -

    Tests

    -

    Procedure

    -

    For each form control that relies on aria-invalid to convey a validation failure:

    +
    JavaScript:
    +

    JavaScript is needed to add aria-invalid, aria-describedby, and class attributes as well as appending the error text. This function is the relevant part of the larger validation script:

    + +
    function addFieldError(field, message) {
    +  const errId = field.id + '-errormsg';
    +  const errDiv = document.createElement('div');
    +  errDiv.className = 'errtext';
    +  errDiv.id = errId;
    +  errDiv.textContent = message;
    +
    +  field.parentNode.appendChild(errDiv);
    +  field.setAttribute('aria-describedby', errId);
    +  field.setAttribute('aria-invalid', 'true');
    +  field.classList.add('error');
    +}
    + +

    Working example: Identifying errors in data format.

    +
    +
    +
    +

    Tests

    +

    Procedure

    +

    For each form control that relies on aria-invalid to convey a validation failure:

      -
    1. Check that aria-invalid is not set to true when a validation failure does not exist.
    2. -
    3. Check that aria-invalid is set to true when a validation failure does exist.
    4. -
    5. Check that the programmatically associated labels / programmatically associated instructional text for the field provide enough information to understand the error.
    6. +
    7. Check that aria-invalid is not set to true when a validation failure does not exist.
    8. +
    9. Check that aria-invalid is set to true when a validation failure does exist.
    10. +
    11. Check that the programmatically associated labels / programmatically associated instructional text for the field provide enough information to understand the error.
    -

    Expected Results

    +
    +

    Expected Results

    • Checks #1-3 are true.
    -
    +

    Resources

    - - - + +
    +
    +

    Resources

    +
    - + + diff --git a/working-examples/aria-alert-identify-errors/index.html b/working-examples/aria-alert-identify-errors/index.html index 84b14f6cf5..9b8d2ce761 100644 --- a/working-examples/aria-alert-identify-errors/index.html +++ b/working-examples/aria-alert-identify-errors/index.html @@ -11,82 +11,101 @@ font:100% / 1.5 sans-serif; } + label{ + display: block; + } + input{ border:1px solid #000; font:inherit; } - + [type=submit]{ background: #666; + font:inherit; color:#fff; } - - + div{ + margin-block:0.5em; + } + -

    Using ARIA live regions or role=alert to identify errors

    -
    - -

    -
    - -

    -

    -
    - -

    -

    -
    - -

    -

    - -

    -
    +
    +

    Using ARIA live regions or role=alert to identify errors

    +
    + +
    + + +
    +
    + + +
    +
    + + +
    +
    + +
    +
    +
    diff --git a/working-examples/aria-alertdialog-identify-errors/index.html b/working-examples/aria-alertdialog-identify-errors/index.html index 7fc69ad453..ef463220e9 100644 --- a/working-examples/aria-alertdialog-identify-errors/index.html +++ b/working-examples/aria-alertdialog-identify-errors/index.html @@ -4,74 +4,97 @@ Using aria-alertdialog to Identify Errors | WCAG 2 - - - - + + + -
    -

    Using aria-alertdialog to Identify Errors

    -

    This example shows how role="alertdialog" can be used to notify someone they have entered invalid information.

    - -
    +
    +

    Using aria-alertdialog to Identify Errors

    +

    This example shows how role="alertdialog" can be used to notify someone they have entered invalid information.

    + +
    + + diff --git a/working-examples/aria-invalid-data-format/error-icon.svg b/working-examples/aria-invalid-data-format/error-icon.svg new file mode 100644 index 0000000000..1983bce2a0 --- /dev/null +++ b/working-examples/aria-invalid-data-format/error-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/working-examples/aria-invalid-data-format/index.html b/working-examples/aria-invalid-data-format/index.html index b07c02bde9..67a326c782 100644 --- a/working-examples/aria-invalid-data-format/index.html +++ b/working-examples/aria-invalid-data-format/index.html @@ -1,91 +1,98 @@ - - -Forms: Using aria-invalid | WCAG 2 - - -

    Forms: Using aria-invalid to identify failed fields

    -

    Features

    -
      -
    • Places an aria-invalid attribute for a field only if the input data is not in the expected format. These also have generic error text alongside but the text is not tied to the field.
    • -
    • Uses a standard generic error message against every field when no data is entered. The error message is tied to the form control using aria-describedby.
    • -
    • The error displayed for a past date (after Jan 1, 2000) is associated with the field via markup
    • -
    • Every form control that fails validation has a red border and an error icon to its right (placed via CSS) for visual reinforcement
    • -
    • Date is not a required field; today's date is filled in as default
    • -
    • The error message placed above the form is within a span tag with role=alert. The SPAN tagg is within an h2 and is read by NVDA and JAWS. VoiceOver reads it in iOS and OSX.
    • -
    -

    - -
    +

    Forms: Using aria-invalid to identify invalid form fields

    -
    -

    -

    -
    -
    -

    -

    -
    -
    -

    -

    -
    -
    -

    -

    -
    -

    - -

    + +

    Log into your account

    + +

    * means required

    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    -

    About the error messages

    -
      -
    • JAWS and NVDA expose a field with aria-invalid=true as "invalid entry".
    • -
    • VoiceOver with Safari on OSX or iOS reads out the field as "invalid data"
    • -
    • The field's label (or other accessible name) is announced before the "invalid" notification.
    • -
    • The field-specific error messages when associated with aria-describedby are read by JAWS and NVDA as one tabs to the fields. VoiceOver reads these automatically only in iOS ... the error message is announced after a slight pause after the label is read. In OSX on a MacBook Pro, the VO+Shift+h key combination causes the error text referenced by aria-describedby to be read by VoiceOver.
    • -
    - - diff --git a/working-examples/aria-invalid-data-format/valcheck.js b/working-examples/aria-invalid-data-format/valcheck.js index 832880c203..fc57996407 100644 --- a/working-examples/aria-invalid-data-format/valcheck.js +++ b/working-examples/aria-invalid-data-format/valcheck.js @@ -1,244 +1,119 @@ -function logincheck() { // For form-alert.htm: using JS alert -if(document.getElementById("lname").value == ""){ -alert("You forgot to enter your last name!"); -return false; -} -if(document.getElementById("usrname").value == ""){ -alert("Please enter your login name"); -return false; -} -if(document.getElementById("pwd").value == ""){ -alert("Please enter your password"); -return false; -} -alert("Sorry cannot login: login and password mismatch. Please try again."); -return true; -} // end of logincheck function - -function showTooltip( tooltip_id ) { -document.getElementById( tooltip_id ).style.display = "inline"; -} -function hideTooltip( tooltip_id ) { -document.getElementById( tooltip_id ).style.display = "none"; -} -function keydownTooltip( event, tooltip_id ) { -var e = window.event || event; -if( e.keyCode == 27 ) { -document.getElementById( tooltip_id ).style.display = "none"; -return browser.stopPropagation( e ); -} else { -return true; -}} // end of function - -function errorAlert () { // For form-alert1.htm: using innerHTML for 1 alert at a time -if(document.getElementById("lname").value == ""){ -document.getElementById("errlist").innerHTML = "
  • You forgot to enter your last name!
  • "; -return false; -} -if(document.getElementById("usrname").value == ""){ -document.getElementById("errlist").innerHTML = "
  • Please enter your login name
  • "; -return false; -} -if(document.getElementById("pwd").value == ""){ -document.getElementById("errlist").innerHTML = "
  • Please enter your password
  • "; -return false; -} -document.getElementById("errlist").innerHTML = "
  • Sorry cannot login: Login name and password mismatch. Please try again.
  • "; -return false; // with innerHTML this too has to be 'false' -} // end of errorAlert function - -function errorAlert1 () {// For form-alert2.htm: using DOM to add error text -// remove error messages already displayed: -var liTag = document.getElementById("errlist") ; -while (liTag.firstChild) { -liTag.removeChild(liTag.firstChild); -} -// done with removing messages -// Next line removes error icons -var myImg = "images/iconError.gif" ; -$('label').find('img').remove() ; -// remove aria-describedby from form controls: -$("input").removeAttr("aria-describedby"); - -// Define arrays -var fields = new Array() ; // holds id of form controls sequentially -fields[0] = "lname"; fields[1] = "usrname"; fields[2] = "pwd"; - -var messages = new Array() ; // Holds error messages -messages[0] = "You forgot to enter your last name!"; -messages[1] = "Please enter your login name"; -messages[2] = "Please enter your password"; - -var errText = new Array() ; // holds actual errors to be listed -var errId = new Array() ; // holds id of list items -var i = 0; var j = 0; errText[i] = ""; -// i can range from 0 to 2 i.e. 1 to 3 error fields -while(j<=2) { -var thisFld = document.getElementById(fields[j]); -if(thisFld.value == ""){ -errText[i] = messages[j] ; -errId[i] = String(i) ; -// Set aria-described for form control: -thisFld.setAttribute("aria-describedby", "list_" +errId[i] ) ; -// Create error icon: -newIcon = document.createElement( "img" ); -$(newIcon).attr({ -src: "images/iconError.gif", -alt: "Error " + String(parseInt(i) +1), -id: "img_" + String(i) +// Attach button click handler and set default date +document.addEventListener('DOMContentLoaded', function() { + // Set default date to today + const startDt = document.getElementById('startDt'); + if (startDt && startDt.value === '') { + startDt.value = formatDate(); + } + + const loginBtn = document.getElementById('next_btn'); + if (loginBtn) { + loginBtn.addEventListener('click', function(event) { + errorAlert(); + }); + } }); -// place error icon within corresponding label element - same as index j. -$(document.getElementsByTagName("LABEL")[j]).append(newIcon); - -// prependImage(fields[j],lbl, errId[i] ); -i++; -} j++; -} // end of while - -if(errText[0] != "") { // means at least one error is present -var j = 0; -while(j < i) { -var liTag = document.createElement("li"); -var node=document.createTextNode(errText[j]); -liTag.appendChild(node); -liTag.setAttribute("id", "list_" + errId[j]); j++; -document.getElementById("errlist").appendChild(liTag); -} // end of while -document.getElementById("errlist").focus() -return false; -} // end of if for errText[0] not blank i.e. errors present -$(document.getElementById("errlist")).html("
  • Sorry cannot login: Login name and password mismatch. Please try again.
  • "); -return false; -} // end of errorAlert1 function - -function prependImage(elementId, strId) { -var image = new Image(); - // Set path and alt properties -image.src = "images/iconError.gif"; -image.alt = "Error " + String(parseInt(strId) +1); -image.id = "img_" + strId; - // Add it to the DOM -// This adds image before the label: -$(document.getElementById(elementId)).parent().prepend(image); - // If all went well, return false so navigation can - // be cancelled based on function outcome -return false; +function errorAlert() { + // Reset state: hide error message and clear field errors + const errorMessage = document.getElementById("error-header"); + errorMessage.hidden = true; + + document.querySelectorAll('div.control div.errtext').forEach(div => div.remove()); + document.querySelectorAll('div.control input').forEach(input => { + input.removeAttribute('aria-describedby'); + input.removeAttribute('aria-invalid'); + input.classList.remove('error'); + }); + + // Field configuration with custom error messages + // isRequired: true means we manually check for empty values + const fields = [ + { id: 'pin4', isRequired: true, emptyMsg: 'Error: PIN is required', errorMsg: 'Error: PIN must be exactly 4 digits' }, + { id: 'email', isRequired: true, emptyMsg: 'Error: Email is required', errorMsg: 'Error: Invalid email address' }, + { id: 'lname', isRequired: true, emptyMsg: 'Error: Last name is required', errorMsg: 'Error: Last name must be 2 or more letters' }, + { id: 'startDt', isRequired: false, errorMsg: 'Error: Invalid date format' } + ]; + + let hasErrors = false; + + fields.forEach((fieldConfig, index) => { + const field = document.getElementById(fieldConfig.id); + const value = field.value.trim(); + + // For startDt: if empty, fill in today's date (not required) + if (fieldConfig.id === 'startDt' && value === '') { + field.value = formatDate(); + return; // Skip validation for this field + } + + // Manual check for required fields + if (fieldConfig.isRequired && value === '') { + addFieldError(field, fieldConfig.emptyMsg); + hasErrors = true; + return; + } + + // Check pattern validity using Constraint Validation API + if (value !== '' && field.validity.patternMismatch) { + addFieldError(field, fieldConfig.errorMsg); + hasErrors = true; + return; + } + + // Check type validity (for email) + if (value !== '' && field.validity.typeMismatch) { + addFieldError(field, fieldConfig.errorMsg); + hasErrors = true; + return; + } + + // Additional check for startDt: date must be in the future + if (fieldConfig.id === 'startDt' && value !== '' && !dateLater(value)) { + addFieldError(field, 'Error: Start date cannot be in the past'); + hasErrors = true; + } + }); + + if (hasErrors) { + errorMessage.hidden = false; + errorMessage.textContent = 'Please fix the errors and retry'; + errorMessage.focus(); + return false; + } +} + +function addFieldError(field, message) { + const errId = field.id + '-errormsg'; + const errDiv = document.createElement('div'); + errDiv.className = 'errtext'; + errDiv.id = errId; + errDiv.textContent = message; + + field.parentNode.appendChild(errDiv); + field.setAttribute('aria-describedby', errId); + field.setAttribute('aria-invalid', 'true'); + field.classList.add('error'); } -function errorAlert2 () {// For form-alert3.htm: using DOM to add error text - -$("div.control span").remove(); // removes generic error message -// remove aria-describedby, aria-invalid and class from form controls: -$("div.control input").removeAttr("aria-describedby aria-invalid class"); -// Define arrays -var fields = new Array() ; // holds id of form controls sequentially -fields[0] = "pin4"; fields[1] = "email"; fields[2] = "lname"; fields[3] = "startDt"; -var eFlag = 0; -var i = 0; var j = 0; -// i can range from 0 to 3 i.e. 1 to 4 error fields -while(j<=3) { -var thisFld = document.getElementById(fields[j]); -if(thisFld.value.length == 0){ -if (j < 3) { // this is not done for start date field -tieErrText(thisFld, i, "Error: Input data missing") ; -i++ ; -eFlag++; -} -if (j == 3) { // means j=3 and date is empty -var dtString =formatDate() ; -$(thisFld).prop("value", dtString); -} -} // end of check for no input data -else if (j == 0 && ( (isNaN(thisFld.value)) || (thisFld.value.length != 4))) { -eFlag = addErrText(thisFld) ; -} // end of if j == 0 -else if (j == 1 ) { // checking email field -// Checks for syntax of an email: -/* This means that the input data must contain an @ sign and at least one dot (.). -Also, the @ must not be the first character of the email address, and the last dot must be present after the @ sign, and minimum 2 characters before the end -*/ -var atpos = thisFld.value.indexOf("@"); -var dotpos = thisFld.value.lastIndexOf("."); -if (atpos<1 || dotpos= thisFld.value.length) { -eFlag = addErrText(thisFld) ; -}} // end of if j==1 -else if (j == 2) { // check last name for non-alpha characters -if (!checkAlpha(thisFld.value)) { -eFlag = addErrText(thisFld) ; -} } // end of j == 2 for last name check - -else if (j == 3 ) { // checking non-empty policy start date field -if(!ValidateDate(thisFld.value)) { -eFlag = addErrText(thisFld) ; -} -else if (!dateLater(thisFld.value)) { -tieErrText(thisFld, i, "Error: Date has passed"); -i++; eFlag++; -} -} // end of j == 3 for date check -j++; -} // end of while -if (eFlag > 0) { -$(document.getElementById("err_final")).text("Sorry cannot login: Please fix the errors and retry").focus(); -return false; -} -$(document.getElementById("err_final")).html("Sorry cannot login: PIN / Email / Name mismatch. Please retry").focus(); -return false; -} // end of errorAlert2 function - -function ValidateDate(dtValue) { -var dtRegex = new RegExp(/\b[01][0-9][\/-][0123][0-9][\/-][2][0]\d{2}\b/); -return dtRegex.test(dtValue); - } - -function checkAlpha(aWord) { -//regular expression defining aword with 2 or more alpha characters: -var alphaRegex = new RegExp(/^[a-zA-Z]{2,}$/) ; -return alphaRegex.test(aWord); - } - - function dateLater(dtStr) { // compares input date string with current date -var yy = dtStr.substr(6) ; -var dd = dtStr.substr(3,2) ; -var mm = dtStr.substr(0,2) ; -var d1 = Date.parse(yy + "-" +mm + "-" + dd); -// var d1 = Date.parse("2013-12-25"); -var dt = new Date() ; -yy =dt.getFullYear(); -mm = dt.getMonth(); mm++; -dd = dt.getDate(); -var d2 = Date.parse(yy + "-" +mm + "-" + dd); -if (d1>= d2) { -return true; -} -return false; -} +const yy = parseInt(dtStr.substring(0, 4), 10); +const mm = parseInt(dtStr.substring(5, 7), 10) - 1; // months are 0-indexed +const dd = parseInt(dtStr.substring(8, 10), 10); +const inputDate = new Date(yy, mm, dd); +inputDate.setHours(0, 0, 0, 0); -function tieErrText(errFld, k, errMsg) { -var errId = "err_" + String(k) ; -// A hidden field with id=spanval is used to hold content of span tag: -$(document.getElementById('spanval')).prop("value", '' + errMsg + '') ; -$(errFld).parent().append(document.getElementById('spanval').value); -// Set aria-described for form control: -errFld.setAttribute("aria-describedby", errId) ; -errFld.setAttribute("class", "error") ; -} +const today = new Date(); +today.setHours(0, 0, 0, 0); -function addErrText(errFld) { -$(errFld).attr("aria-invalid", "true").attr("class", "error"); -// Suffix error text: -$(errFld).parent().append('Error: Incorrect data'); -return 1; +// Returns true if date is today or in the future (valid) +// Returns false if date is before today (error) +return inputDate >= today; } function formatDate() { -var dt1 = new Date(); -var mm = dt1.getMonth(); mm++ ; -if (mm < 10) { mm ="0" + mm; } -var dd = dt1.getDate() ; -if (dd < 10) { dd ="0" + dd; } -var dtStr = mm + "/" + dd +"/" + dt1.getFullYear(); -return dtStr; +const dt1 = new Date(); +const mm = String(dt1.getMonth() + 1).padStart(2, '0'); +const dd = String(dt1.getDate()).padStart(2, '0'); +return dt1.getFullYear() + "-" + mm + "-" + dd; } diff --git a/working-examples/aria-invalid-required-fields/index.html b/working-examples/aria-invalid-required-fields/index.html index 4f10dde992..4fbafe7f59 100644 --- a/working-examples/aria-invalid-required-fields/index.html +++ b/working-examples/aria-invalid-required-fields/index.html @@ -1,68 +1,109 @@ - Input Error Notification with aria-live=assertive and aria-invalid using jQuery | WCAG 2 + - +Input Error Notification with aria-live=assertive and aria-invalid | WCAG 2 + - - -

    -
    -

    -
    - -

    -

    -
    - -

    -

    -
    - -

    -

    - -

    +

    Input Error Notification with aria-live=assertive and aria-invalid

    + +

    +
    + + +
    +
    + + +
    +
    + + +
    +
    + +