PageSourceSearch

https://www.saintpolsurternoise.fr/wp-content/plugins/hob-formchat-pro/assets/js/hob-rgaa.js?ver=1.29

js saintpolsurternoise.fr collected 2026-10-02 21:05:10 UTC 4,070 bytes, 111 lines download raw bytes

1/**
2 * HOB FormChat Pro — RGAA Accessibility Runtime
3 *
4 * Handles dynamic RGAA compliance:
5 * - aria-invalid toggle on validation errors
6 * - Error message injection with role="alert"
7 * - Focus management on validation failure
8 * - aria-live announcements for dynamic zones (calculators, AI)
9 * - Focus-visible outline enforcement
10 *
11 * @package HOB_FormChat_Pro
12 * @since   1.21
13 */
14(function () {
15  'use strict';
16
17  document.addEventListener('DOMContentLoaded', function () {
18    var wrappers = document.querySelectorAll('.hob-fc-rgaa');
19    if (!wrappers.length) return;
20
21    wrappers.forEach(function (wrapper) {
22      initRgaa(wrapper);
23    });
24  });
25
26  function initRgaa(wrapper) {
27    var form = wrapper.querySelector('.hob-fc-form');
28    if (!form) return;
29
30    var isHighContrast = wrapper.dataset.rgaaContrast === 'high';
31
32    // 1. Focus-visible: add class for CSS outline.
33    form.addEventListener('focusin', function (e) {
34      var el = e.target;
35      if (el.matches('input, select, textarea, button, [tabindex]')) {
36        el.classList.add('hob-fc-focus-visible');
37      }
38    });
39    form.addEventListener('focusout', function (e) {
40      e.target.classList.remove('hob-fc-focus-visible');
41    });
42
43    // 2. Validation: set aria-invalid on blur for required fields.
44    form.addEventListener('focusout', function (e) {
45      var el = e.target;
46      if (!el.matches('input, select, textarea')) return;
47      if (!el.hasAttribute('aria-required') && !el.required) return;
48
49      var isEmpty = !el.value || (el.value.trim() === '');
50      var fieldId = el.id ? el.id.replace('hob-field-', '') : '';
51      var errorEl = fieldId ? document.getElementById('hob-error-' + fieldId) : null;
52
53      if (isEmpty) {
54        el.setAttribute('aria-invalid', 'true');
55        if (errorEl) {
56          var label = '';
57          var labelEl = form.querySelector('label[for="' + el.id + '"]');
58          if (labelEl) label = labelEl.textContent.replace(/\s*\*\s*$/, '').trim();
59          errorEl.textContent = label
60            ? 'Le champ ' + label + ' est obligatoire.'
61            : 'Ce champ est obligatoire.';
62          errorEl.style.display = '';
63        }
64      } else {
65        el.removeAttribute('aria-invalid');
66        if (errorEl) {
67          errorEl.textContent = '';
68          errorEl.style.display = 'none';
69        }
70      }
71    });
72
73    // 3. On submit: validate and focus first invalid field.
74    form.addEventListener('submit', function (e) {
75      var firstInvalid = form.querySelector('[aria-invalid="true"], :invalid');
76      if (firstInvalid) {
77        e.preventDefault();
78        firstInvalid.focus();
79
80        // Announce to screen readers.
81        var liveZone = wrapper.querySelector('.hob-fc-rgaa-announce');
82        if (!liveZone) {
83          liveZone = document.createElement('div');
84          liveZone.className = 'hob-fc-rgaa-announce hob-fc-sr-only';
85          liveZone.setAttribute('aria-live', 'assertive');
86          liveZone.setAttribute('role', 'status');
87          wrapper.appendChild(liveZone);
88        }
89        var invalidCount = form.querySelectorAll('[aria-invalid="true"]').length
90          || form.querySelectorAll(':invalid').length;
91        liveZone.textContent = invalidCount + ' erreur' + (invalidCount > 1 ? 's' : '')
92          + ' dans le formulaire. Veuillez corriger ' + (invalidCount > 1 ? 'les' : 'le')
93          + ' champ' + (invalidCount > 1 ? 's' : '') + ' en erreur.';
94      }
95    });
96
97    // 4. Dynamic zones: add aria-live to calculator results.
98    var calcOutputs = form.querySelectorAll('.hob-fc-calculator-result, [data-calc-formula]');
99    calcOutputs.forEach(function (el) {
100      if (!el.getAttribute('aria-live')) {
101        el.setAttribute('aria-live', 'polite');
102        el.setAttribute('aria-atomic', 'true');
103      }
104    });
105
106    // 5. High contrast mode: add data attribute for CSS.
107    if (isHighContrast) {
108      wrapper.classList.add('hob-fc-high-contrast');
109    }
110  }
111})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.