Oorzaak (kort)
Hello elementor (en wordpress thema’s in het algemeen) gebruiken vaak een skiplink die naar #content wijst. Op elementor templates zoals archive ontbreekt echter vaak:
- Een main landmark, en/of
- Een element met
id="content".
Daardoor kan de browser de in-page link niet correct afhandelen en verplaatst de toetsenbordfocus niet naar de hoofdinhoud.
Oplossing
Onderstaande code doet twee dingen:
- Maakt (indien nodig) een
<main id="content" tabindex="-1">aan en wrapt daar de elementor content in. Dit werkt voor: pages, single posts, archives, search results en 404 pagina’s. - Verplaatst focus betrouwbaar naar het skipdoel en scrollt netjes naar de content (met respect voor
prefers-reduced-motion).
Implementatie (aanrader)
Plaats de code site-wide via: elementor pro → custom code → add new
- location: body end (
</body> - end) - active: yes
- display conditions: entire site
Geen elementor pro? Gebruik een code snippet plugin (bijv. code snippets) en voeg de js/css in via de site-wide header/footer-injectie.
Code (js + css)
Plak onderstaande code als één blok in je editor.
<script>
(function () {
function getHeaderEl() {
return document.querySelector('header, .elementor-location-header');
}
function getElementorContentRoot() {
return document.querySelector(
'[data-elementor-type="wp-page"],' +
'[data-elementor-type="wp-post"],' +
'[data-elementor-type="single"],' +
'[data-elementor-type="single-post"],' +
'[data-elementor-type="archive"],' +
'[data-elementor-type="search-results"],' +
'[data-elementor-type="error-404"]'
);
}
function ensureMainTarget() {
let main = document.querySelector('main, [role="main"]');
if (!main) {
const root = getElementorContentRoot();
if (!root) return null;
main = document.createElement('main');
main.id = 'content';
main.tabIndex = -1;
root.parentNode.insertBefore(main, root);
main.appendChild(root);
}
if (!main.id || main.id !== 'content') main.id = 'content';
if (!main.hasAttribute('tabindex')) main.tabIndex = -1;
return main;
}
function setHeaderOffsetVar() {
const header = getHeaderEl();
const h = header ? Math.ceil(header.getBoundingClientRect().height) : 0;
document.documentElement.style.setProperty('--skip-offset', h + 'px');
}
function init() {
const target = ensureMainTarget();
setHeaderOffsetVar();
window.addEventListener('resize', setHeaderOffsetVar);
const skipLinks = document.querySelectorAll('a.skip-link[href^="#"], a[href="#content"]');
if (!target || !skipLinks.length) return;
skipLinks.forEach(function (link) {
link.setAttribute('href', '#content');
link.addEventListener('click', function () {
setTimeout(function () {
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
target.focus({ preventScroll: true });
target.scrollIntoView({ block: 'start', behavior: reduceMotion ? 'auto' : 'smooth' });
}, 0);
});
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
</script>
<style>
.skip-link {
position: absolute;
inset-block-start: 0.5rem;
inset-inline-start: -9999px;
z-index: 999999;
}
.skip-link:focus {
inset-inline-start: 0.5rem;
background: #ffeb85;
color: #000;
padding: 0.75rem 1rem;
outline: 2px solid #000;
text-decoration: none;
}
#content {
scroll-margin-top: calc(var(--skip-offset, 0px) + 16px);
}
</style>
Waarom tabindex=”-1″?
Veel doel-elementen (zoals main) zijn niet standaard focusable. Zonder focus verplaatst een screenreader of toetsenbordfocus soms niet correct, zelfs als er wel gescrold wordt. tabindex="-1" maakt het element focusbaar zonder dat het in de normale tabvolgorde komt.
Testen (wcag 2.4.1)
- Herlaad de pagina en druk één keer op tab. De skiplink moet zichtbaar worden.
- Druk op enter. De pagina moet naar de hoofdinhoud springen en de focus moet daar staan.
- Druk opnieuw op tab: je moet verder tabben in de content, niet terug naar het menu.
Veelvoorkomende valkuilen
- Dubbele skiplinks: hello en elementor kunnen er beide een outputten.
- Canvas template: Deze fix werkt ook voor canvas templates.
- Sticky header: De scroll-margin voorkomt dat content wegvalt.
function copyCode(button) {
const code = document.getElementById('code-block').innerText;
navigator.clipboard.writeText(code).then(() => {
button.innerText = 'Gekopieerd!';
setTimeout(() => {
button.innerText = 'Kopieer code';
}, 2000);
});
}