Autot
Polo
Poista
Micra
Poista
Corsa
Poista
Siirrä fokus poiston jälkeen
Fokus on nyt:
-
CSS
/* Esikatselualue määrittelee sekä taustan että tekstin värin, jotta pari on varmasti luettava kummassakin teemassa. */ #alue { background-color: #ffffff; color: #1a1a1a; padding: 0.5rem; } @media (prefers-color-scheme: dark) { #alue { background-color: #1e1e1e; color: #f0f0f0; } } /* Ruudunlukijalle tarkoitettu alue: ei näy, mutta luetaan. */ .vain-ruudunlukijalle { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } #lista { list-style: none; padding: 0; max-width: 20rem; } #lista li { display: flex; justify-content: space-between; gap: 0.5rem; padding: 0.3rem; border-bottom: 1px solid currentColor; } /* Fokusreunusta ei saa poistaa. Jos selaimen oma reunus ei sovi ulkoasuun, se korvataan omalla - ja :focus-visible näyttää sen silloin kun sitä tarvitaan eli näppäimistöllä. */ :focus-visible { outline: 3px solid #0060c0; outline-offset: 2px; } #otsikko:focus-visible { outline-style: dashed; }
HTML
<div id="alue"> <h2 id="otsikko" tabindex="-1">Autot</h2> <ul id="lista"> <li>Polo <button type="button">Poista</button></li> <li>Micra <button type="button">Poista</button></li> <li>Corsa <button type="button">Poista</button></li> </ul> <p> <label> <input type="checkbox" id="oikein" checked="checked" /> Siirrä fokus poiston jälkeen </label> </p> <p>Fokus on nyt: <output id="tila">-</output></p> <p id="ilmoitus" aria-live="polite" class="vain-ruudunlukijalle"></p> </div>
Javascript
/* Koodikoneen Run-painike suorittaa koodin uudelleen, jolloin samat käsittelijät liitettäisiin toistamiseen. Elementti korvataan kopiollaan: kopio ei peri tapahtumankäsittelijöitä. */ const puhdista = function (elementti) { const kopio = elementti.cloneNode(true); elementti.replaceWith(kopio); return kopio; }; /* Kokeile: liiku sarkaimella poistopainikkeeseen ja paina enteriä. Ota sen jälkeen valinta pois päältä ja tee sama - fokus katoaa dokumentin alkuun, koska elementti johon se osoitti lakkasi olemasta. */ const lista = puhdista(document.getElementById("lista")); const otsikko = document.getElementById("otsikko"); const ilmoitus = document.getElementById("ilmoitus"); const tila = document.getElementById("tila"); /* Näytetään koko ajan, missä fokus on. document.activeElement kertoo sen. */ const naytaFokus = function () { const e = document.activeElement; tila.textContent = e === document.body ? "ei missään (body)" : `${e.nodeName.toLowerCase()} — "${e.textContent.trim().slice(0, 20)}"`; }; document.addEventListener("focusin", naytaFokus); document.addEventListener("focusout", () => setTimeout(naytaFokus, 0)); naytaFokus(); lista.addEventListener("click", function (e) { const nappi = e.target.closest("button"); if (nappi === null) { return; } const rivi = nappi.closest("li"); const nimi = rivi.textContent.replace("Poista", "").trim(); /* Kohde on otettava talteen ENNEN poistoa: poistetun rivin naapureita ei enää löydä jälkikäteen. */ const seuraava = rivi.nextElementSibling ?? rivi.previousElementSibling; rivi.remove(); /* Muutoksesta kerrotaan aria-live-alueella */ ilmoitus.textContent = `${nimi} poistettu`; console.log(ilmoitus.textContent); if (!document.getElementById("oikein").checked) { console.log("fokus jätettiin tarkoituksella siirtämättä"); naytaFokus(); return; } if (seuraava !== null) { seuraava.querySelector("button").focus(); } else { /* Lista tyhjeni. Otsikko ei ole luontaisesti fokusoitava, joten sillä on tabindex="-1": se ei tule näppäinkiertoon mutta voidaan fokusoida ohjelmallisesti. */ otsikko.focus(); } });
Konsoli