100 Ct BX

/** * Fastening Specialists — Accessibility fix C1 * Sitebulb / axe-core: "Form elements must have labels" (866 pages) * * CAUSE * The product tables on category pages are built by the "WooCommerce Product * Table" plugin (Barn2) and loaded via AJAX (DataTables). The controls it * generates have no usable label, so axe-core flags them on every category * page that has a table: * - the "Select all" / per-row bulk-select checkboxes (class wpt-bulk-select) * - the per-row quantity fields in the Buy column * - the table's search box / "show entries" dropdown * The Barn2 markup even prints a but never connects it to the checkbox (no for=/id=), so it * doesn't count as a label. * * Because the rows are built by JavaScript after page load, this must be * fixed in JavaScript — a PHP template/filter override will not catch the * AJAX-rendered rows. This script adds the missing accessible names after the * table loads and after every redraw (search, sort, pagination). * * DEPLOY — pick ONE: * A) Elementor Pro -> Custom Code -> Add New. Paste everything below into * the code box, set Location to "End of page / before body close". * B) Child theme (most reliable): save this file as * wp-content/themes/hello-theme-child-master/js/c1-form-input-labels-fix.js * and enqueue it from functions.php (PHP snippet at the bottom of this * file). An external .js file avoids the inline-script pitfall entirely. * * Safe to leave in place permanently — it only touches controls that are * still missing an accessible name, and no-ops if Barn2 fixes this upstream. */ (function () { 'use strict'; function needsName(el) { if (el.getAttribute('aria-label')) return false; if (el.getAttribute('aria-labelledby')) return false; if (el.getAttribute('title')) return false; if (el.closest('label')) return false; if (el.id) { var sel = (window.CSS && CSS.escape) ? CSS.escape(el.id) : el.id; if (document.querySelector('label[for="' + sel + '"]')) return false; } return true; } function fix() { document.querySelectorAll('table.wc-product-table').forEach(function (table) { // 1. "Select all" checkbox — connect the visible label Barn2 already // prints but never associates with the input. table.querySelectorAll('span.wpt-bulk-select-wrap').forEach(function (wrap) { var label = wrap.querySelector('label.wpt-bulk-select-label'); var box = wrap.querySelector('input.wpt-bulk-select'); if (label && box && !box.id) { var id = 'wpt-bulk-all-' + Math.random().toString(36).slice(2, 8); box.id = id; label.setAttribute('for', id); } }); // 2. Per-row bulk-select checkboxes (no visible label text). table.querySelectorAll('tbody input.wpt-bulk-select').forEach(function (box) { if (needsName(box)) box.setAttribute('aria-label', 'Select this product'); }); // 3. Quantity fields in the Buy column — name them after the product. table.querySelectorAll('tbody input.qty, tbody input[type="number"]').forEach(function (qty) { if (!needsName(qty)) return; var firstCell = qty.closest('tr') && qty.closest('tr').querySelector('td'); var product = firstCell ? firstCell.textContent.replace(/\s+/g, ' ').trim().slice(0, 80) : ''; qty.setAttribute('aria-label', product ? 'Quantity for ' + product : 'Quantity'); }); // 4. The table's own search box / "show entries" dropdown. var wrapper = table.closest('.dataTables_wrapper'); if (wrapper) { wrapper.querySelectorAll('input[type="search"], .dataTables_filter input').forEach(function (s) { if (needsName(s)) s.setAttribute('aria-label', s.getAttribute('placeholder') || 'Search products'); }); wrapper.querySelectorAll('.dataTables_length select').forEach(function (sel) { if (needsName(sel)) sel.setAttribute('aria-label', 'Number of products to show'); }); } }); } // Run on initial build and after every AJAX redraw / sort / page change. if (window.jQuery) { jQuery(document).on('init.dt draw.dt', 'table.wc-product-table', fix); } document.addEventListener('DOMContentLoaded', fix); window.addEventListener('load', function () { setTimeout(fix, 1200); }); })(); /* --------------------------------------------------------------------------- * functions.php enqueue (Option B) — add to the child theme's functions.php: * * add_action( 'wp_enqueue_scripts', function () { * wp_enqueue_script( * 'fs-a11y-form-labels', * get_stylesheet_directory_uri() . '/js/c1-form-input-labels-fix.js', * array( 'jquery' ), * '1.0.0', * true // load in footer * ); * } ); * ------------------------------------------------------------------------- */