Änderungen an Preis, Variante, Bestand und Warenkorb müssen angesagt werden
Wählt jemand eine Größe und ändern sich dadurch Preis oder Verfügbarkeit, oder legt jemand ein Produkt in den Warenkorb, bemerken sehende Kundinnen und Kunden die Änderung. Menschen, die einen Screenreader nutzen, hören nichts und wissen daher nicht, dass das Produkt hinzugefügt wurde oder dass die gewählte Größe ausverkauft ist.
- Regel-ID
- vs-live-updates
- Quelle
- Validiscan-Prüfung
- Auswirkung
- Schwerwiegend
- Korrekturstufe
- AUTO
- auf Shopify, Shopware, WooCommerce und Magento
- Status
- wird ausgeführt
Was geprüft wird
Erkennt Änderungen an Preis, Variante, Bestand und Warenkorb auf Produkt- und Warenkorbseiten, die ohne Live-Region erfolgen, sowie aria-live-Regionen, die die ganze Seite umschließen (4.1.3).
Die Prüfung läuft nur auf Produkt- und Warenkorbseiten. Gemeldet wird jedes aria-live="polite" oder "assertive" an html, body, main oder an einem Element, das main enthält.
Die Prüfung ändert ein einziges Bedienelement außerhalb von Header, Navigation, Footer und Dialogen und unterbindet dabei das Absenden von Formularen. Auf der Produktseite ist das das erste select für Varianten (benannt nach Größe, Farbe oder Ähnlichem oder im Produktbereich gelegen), sonst eine Radiobutton-Gruppe für Varianten, sonst Farbfeld-Schaltflächen, von denen eine ausgewählt ist. Auf der Warenkorbseite ist es das erste Mengenfeld, dessen Wert die Prüfung um eins ändert.
Vorher und nachher liest die Prüfung Texte von bis zu 120 Zeichen, deren itemprop, Klasse oder id auf einen Preis, den Bestand, die Anzahl im Warenkorb oder eine Summe hindeutet, außerdem die Schaltfläche „In den Warenkorb“. Die Seite hat bis zu 2 Sekunden Zeit, zur Ruhe zu kommen. Ein geänderter sichtbarer Text wird gemeldet, es sei denn, er liegt in einer Live-Region oder enthält eine (aria-live="polite" oder "assertive", role="status", "alert", "log", output), oder eine andere Live-Region hat eine Änderung angesagt (bei Preis und Bestand eine, die den neuen Wert enthält). Eine Änderung, die beim Zurücksetzen des Bedienelements nicht wieder verschwindet, landet bei „zu prüfen“ und wird derzeit nicht zum Befund.
So wird es behoben
Ergänzen Sie eine kleine, dauerhaft vorhandene Live-Region (role="status" oder aria-live="polite") für Preis, Bestand und Anzahl im Warenkorb, und aktualisieren Sie ihren Text, wenn sich die Variante oder der Warenkorb ändert. Setzen Sie aria-live nicht auf große Container oder die ganze Seite, sonst lesen Screenreader alles erneut vor.
WCAG-Erfolgskriterien
- 4.1.3 Status Messages, Stufe AA
Abschnitte der EN 301 549
| WCAG-Kriterium | Version 3.2.1 | Version 4.1.1 |
|---|---|---|
| 4.1.3 | 9.4.1.3 | 9.4.1.3 |
Wo ein Rezept es behebt
Shopify: Rezept live-region-cart-variant
Vorher und nachher anzeigen (1)
sections/main-cart.liquid Änderungen an sections/main-cart.liquid: 1 hinzugefügt, 1 entfernt
| Zeile vorher | Zeile nachher | Änderung | Code |
|---|---|---|---|
| 37 | 37 | ||
| 38 | 38 | <div class="cart__footer"> | |
| 39 | 39 | <h2 class="cart__summary-title">Order summary</h2> | |
| 40 | Entfernt | <p class="totals"> | |
| 40 | Hinzugefügt | <p class="totals" role="status"> | |
| 41 | 41 | <span class="totals__label">{{ 'sections.cart.subtotal' | t }}</span> | |
| 42 | 42 | <span class="totals__value" data-cart-subtotal>{{ cart.total_price | money_with_currency }}</span> | |
| 43 | 43 | </p> |
sections/main-product.liquid Änderungen an sections/main-product.liquid: 1 hinzugefügt, 1 entfernt
| Zeile vorher | Zeile nachher | Änderung | Code |
|---|---|---|---|
| 71 | 71 | </div> | |
| 72 | 72 | {%- endif %} | |
| 73 | 73 | ||
| 74 | Entfernt | <div class="product__price" data-price-container> | |
| 74 | Hinzugefügt | <div class="product__price" data-price-container role="status"> | |
| 75 | 75 | {% render 'price', product: product, use_variant: true %} | |
| 76 | 76 | <p class="product__inventory" data-stock>{% if current_variant.available %}{{ 'products.product.in_stock' | t }}{% else %}{{ 'products.product.out_of_stock' | t }}{% endif %}</p> | |
| 77 | 77 | <p class="product__sku">{{ 'products.product.sku' | t }}: <span data-sku>{{ current_variant.sku }}</span></p> |
Shopware: Rezept live-region-cart-variant
Vorher und nachher anzeigen (1)
Resources/views/storefront/page/checkout/cart/index.html.twig Änderungen an Resources/views/storefront/page/checkout/cart/index.html.twig: 2 hinzugefügt, 2 entfernt
| Zeile vorher | Zeile nachher | Änderung | Code |
|---|---|---|---|
| 59 | 59 | <span class="line-item-unit-price-label">{{ 'checkout.cartHeaderUnitPrice'|trans }}:</span> | |
| 60 | 60 | <span class="line-item-unit-price-value">{{ item.unitPrice|currency }}*</span> | |
| 61 | 61 | </div> | |
| 62 | Entfernt | <div class="line-item-total-price"> | |
| 62 | Hinzugefügt | <div class="line-item-total-price" aria-live="polite" aria-atomic="true"> | |
| 63 | 63 | <span class="line-item-total-price-label">{{ 'checkout.cartHeaderTotalPrice'|trans }}:</span> | |
| 64 | 64 | <span class="line-item-total-price-value" data-line-total>{{ item.totalPrice|currency }}*</span> | |
| 65 | 65 | </div> | |
| 88 | 88 | <div class="col-lg-4 checkout-aside"> | |
| 89 | 89 | <div class="checkout-aside-summary"> | |
| 90 | 90 | <h2 class="checkout-aside-summary-header">{{ 'checkout.summaryHeader'|trans }}</h2> | |
| 91 | Entfernt | <div class="checkout-aside-summary-totals" data-shipping-threshold="{{ shop.shippingThreshold }}" data-shipping-costs="{{ shop.shippingCosts }}"> | |
| 91 | Hinzugefügt | <div class="checkout-aside-summary-totals" data-shipping-threshold="{{ shop.shippingThreshold }}" data-shipping-costs="{{ shop.shippingCosts }}" aria-live="polite" aria-atomic="true"> | |
| 92 | 92 | <dl class="checkout-aside-summary-list"> | |
| 93 | 93 | <dt class="checkout-aside-summary-label">{{ 'checkout.summaryPositionPrice'|trans }}</dt> | |
| 94 | 94 | <dd class="checkout-aside-summary-value" data-cart-subtotal>{{ cart.subtotal|currency }}*</dd> |
Resources/views/storefront/page/product-detail/buy-widget.html.twig Änderungen an Resources/views/storefront/page/product-detail/buy-widget.html.twig: 1 hinzugefügt, 1 entfernt
| Zeile vorher | Zeile nachher | Änderung | Code |
|---|---|---|---|
| 17 | 17 | {% endblock %} | |
| 18 | 18 | ||
| 19 | 19 | {% block buy_widget_price %} | |
| 20 | Entfernt | <div class="product-detail-price-container"> | |
| 20 | Hinzugefügt | <div class="product-detail-price-container" aria-live="polite" aria-atomic="true"> | |
| 21 | 21 | <meta itemprop="priceCurrency" content="{{ shop.currency.isoCode }}"> | |
| 22 | 22 | <meta itemprop="price" content="{{ product.price }}"> | |
| 23 | 23 | <p class="product-detail-price{% if product.listPrice %} with-list-price{% endif %}" data-price-display>{{ product.price|currency }}*</p> |
WooCommerce: Rezept live-region-cart-variant
Vorher und nachher anzeigen (2)
assets/js/theme.js Änderungen an assets/js/theme.js: 15 hinzugefügt, 0 entfernt
| Zeile vorher | Zeile nachher | Änderung | Code |
|---|---|---|---|
| 255 | 255 | initCheckout(); | |
| 256 | 256 | initShowPassword(); | |
| 257 | 257 | })(); | |
| 258 | Hinzugefügt | ||
| 259 | Hinzugefügt | /* validiscan:live-region-cart-variant */ | |
| 260 | Hinzugefügt | // Announces cart changes in the status region next to the header cart (WCAG 4.1.3). | |
| 261 | Hinzugefügt | (function () { | |
| 262 | Hinzugefügt | const region = document.querySelector('[data-kb-cart-status]'); | |
| 263 | Hinzugefügt | if (!region || !window.jQuery) return; | |
| 264 | Hinzugefügt | window.jQuery(document.body).on('added_to_cart removed_from_cart', (event) => { | |
| 265 | Hinzugefügt | const message = region.getAttribute(event.type === 'added_to_cart' ? 'data-added' : 'data-removed'); | |
| 266 | Hinzugefügt | region.textContent = ''; | |
| 267 | Hinzugefügt | window.setTimeout(() => { | |
| 268 | Hinzugefügt | region.textContent = message || ''; | |
| 269 | Hinzugefügt | }, 100); | |
| 270 | Hinzugefügt | }); | |
| 271 | Hinzugefügt | })(); | |
| 272 | Hinzugefügt | /* /validiscan:live-region-cart-variant */ |
header.php Änderungen an header.php: 1 hinzugefügt, 0 entfernt
| Zeile vorher | Zeile nachher | Änderung | Code |
|---|---|---|---|
| 67 | 67 | <?php kb_icon( 'bag' ); ?><span class="kb-cart-amount"><?php echo wp_kses_post( WC()->cart->get_cart_subtotal() ); ?></span> | |
| 68 | 68 | <span class="count"><?php echo esc_html( sprintf( _n( '%d item', '%d items', WC()->cart->get_cart_contents_count(), 'storefront' ), WC()->cart->get_cart_contents_count() ) ); ?></span> | |
| 69 | 69 | </a> | |
| 70 | Hinzugefügt | <span class="screen-reader-text" role="status" data-kb-cart-status data-added="<?php esc_attr_e( 'Product added to your cart.', 'kestrel-birch' ); ?>" data-removed="<?php esc_attr_e( 'Product removed from your cart.', 'kestrel-birch' ); ?>"></span> | |
| 70 | 71 | </li> | |
| 71 | 72 | </ul> | |
| 72 | 73 | </div> |
woocommerce/single-product/add-to-cart/variable.php Änderungen an woocommerce/single-product/add-to-cart/variable.php: 1 hinzugefügt, 1 entfernt
| Zeile vorher | Zeile nachher | Änderung | Code |
|---|---|---|---|
| 65 | 65 | </table> | |
| 66 | 66 | ||
| 67 | 67 | <div class="single_variation_wrap"> | |
| 68 | Entfernt | <div class="woocommerce-variation single_variation"> | |
| 68 | Hinzugefügt | <div class="woocommerce-variation single_variation" aria-live="polite" aria-atomic="true"> | |
| 69 | 69 | <div class="woocommerce-variation-description"></div> | |
| 70 | 70 | <div class="woocommerce-variation-price"><span class="price"><?php echo wp_kses_post( wc_price( $kb_selected['display_price'] ) ); ?></span></div> | |
| 71 | 71 | <div class="woocommerce-variation-availability"><?php echo wp_kses_post( $kb_selected['availability_html'] ); ?></div> |
Magento: Rezept live-region-cart-variant
Vorher und nachher anzeigen (1)
app/design/frontend/Validiscan/fixture/Magento_Catalog/templates/product/price/final_price.phtml Änderungen an app/design/frontend/Validiscan/fixture/Magento_Catalog/templates/product/price/final_price.phtml: 1 hinzugefügt, 1 entfernt
| Zeile vorher | Zeile nachher | Änderung | Code |
|---|---|---|---|
| 13 | 13 | // Fixed variant: the product-page box carries aria-live="polite" aria-atomic="true" and the stock | |
| 14 | 14 | // message (product/view/type/default.phtml) is role="status". | |
| 15 | 15 | ?> | |
| 16 | Entfernt | <div class="price-box price-final_price" data-role="priceBox" data-product-id="<?= (int) $product->getId() ?>" data-price-box="product-id-<?= (int) $product->getId() ?>"<?php if ($isProductPage): ?> data-base-price="<?= (float) $amount ?>"<?php endif; ?>> | |
| 16 | Hinzugefügt | <div class="price-box price-final_price" data-role="priceBox" data-product-id="<?= (int) $product->getId() ?>" data-price-box="product-id-<?= (int) $product->getId() ?>"<?php if ($isProductPage): ?> data-base-price="<?= (float) $amount ?>"<?php endif; ?> aria-live="polite" aria-atomic="true"> | |
| 17 | 17 | <span class="normal-price"><span class="price-container price-final_price tax weee"><span id="product-price-<?= (int) $product->getId() ?>" data-price-amount="<?= (float) $amount ?>" data-price-type="finalPrice" class="price-wrapper"><span class="price"><?= $escaper->escapeHtml($block->renderAmount($block->getPriceType('final_price')->getAmount(), ['display_label' => false])) ?></span></span></span></span> | |
| 18 | 18 | </div> |
app/design/frontend/Validiscan/fixture/Magento_Catalog/templates/product/view/type/default.phtml Änderungen an app/design/frontend/Validiscan/fixture/Magento_Catalog/templates/product/view/type/default.phtml: 1 hinzugefügt, 1 entfernt
| Zeile vorher | Zeile nachher | Änderung | Code |
|---|---|---|---|
| 11 | 11 | ?> | |
| 12 | 12 | <div class="product-info-stock-sku"> | |
| 13 | 13 | <?php if ($block->displayProductStockStatus()): ?> | |
| 14 | Entfernt | <div class="stock available" title="<?= $escaper->escapeHtmlAttr(__('Availability')) ?>"><span><?= $escaper->escapeHtml(__('In stock')) ?></span></div> | |
| 14 | Hinzugefügt | <div class="stock available" title="<?= $escaper->escapeHtmlAttr(__('Availability')) ?>" role="status"><span><?= $escaper->escapeHtml(__('In stock')) ?></span></div> | |
| 15 | 15 | <?php endif; ?> | |
| 16 | 16 | <div class="product attribute sku"><strong class="type"><?= $escaper->escapeHtml(__('SKU')) ?></strong> <div class="value" itemprop="sku"><?= $escaper->escapeHtml($product->getSku()) ?></div></div> | |
| 17 | 17 | </div> |
Beginnen Sie mit einem kostenlosen Check Ihres Shops.
Senden Sie die Adresse Ihres Shops. Validiscan scannt ihn und antwortet mit jedem Fehler, den der Scan nachweisen kann, jeweils mit der Seite und dem Code.
Kostenlosen Check anfordernOder schreiben Sie an info@validiscan.com.
Was der kostenlose Check umfasst und was nicht- Sie senden
- die Adresse Ihres Shops
- Gescannte Seiten
- bis zu 15
- Sie erhalten
- jeden Fehler, den der Scan nachweisen kann, mit Seite und Code
- In Ihrem Shop
- nur öffentliche Seiten; keine Anmeldung, nichts gekauft, kein Formular abgeschickt
- Was er nicht ist
- ein vollständiges Audit