Wpadki i wypadki #11

Oto kolejny odcinek Wpadek i wypadków!

Dzisiaj zajmiemy się wzorcem, który ostatnimi czasy zdobywa naprawdę sporą popularność: umieszczaniem linków w przyciskach i przycisków w linkach. Mówię tutaj o konstrukcjach typu:

<a href="#">
	<button>Jestem przyciskiem w linku</button>
</a>
<button>
	<a href="#">Jestem linkiem w przycisku</a>
</button>

Tego typu konstrukcje są złe z kilku powodów:

  • Z perspektywy semantyki nie mają absolutnie żadnego sensu. Przyciski to elementy służące do zupełnie innych celów niż linki. Specyfikacja HTML określa przyciski jako elementy zdolne do wysłania formularzy. Jedyny przykład dla przycisku, który nie jest związany z wysłaniem formularza, to kod przycisku otwierającego okienko alert. Z kolei linki to elementy reprezentujące hiperlinki, czyli odsyłacze do innych zasobów (wewnątrz i na zewnątrz danej strony), których kliknięcie powoduje nawigację do tych zasobów.

    Inaczej mówiąc: przyciski służą do wykonania akcji, a linki – do przejścia na inną stronę/inne miejsce na danej stronie. Połączenie tych dwóch elementów próbuje pogodzić ze sobą dwa wzajemnie się wykluczające znaczenia.

  • Takie łączenie elementów jest wprost zabronione przez specyfikację HTML, przy użyciu tzw. modelów zawartości. Model zawartości to po prostu lista elementów, jakie można umieścić w danym elemencie. Dla elementu a model jest zdefiniowany następująco:

    Transparent, but there must be no interactive content descendant, a element descendant, or descendant with the tabindex attribute specified.

    [Przezroczysty, ale nie może zawierać potomka będącego treścią interaktywną, elementu a lub potomka ze zdefiniowanym atrybutem [tabindex].]

    Z kolei dla elementu button model zawartości jest zdefiniowany tak:

    Phrasing content, but there must be no interactive content descendant and no descendant with the tabindex attribute specified.

    [Zawartość frazowa, ale nie może zawierać potomka będącego treścią interaktywną lub potomka ze zdefiniowanym atrybutem [tabindex].]

    Jak widać, definicje modelów dla obu elementów są bardzo podobne. Hasłem-kluczem jest treść interaktywna, którą specyfikacja HTML definiuje w taki sposób:

    Interactive content is content that is specifically intended for user interaction.

    a (if the href attribute is present), audio (if the controls attribute is present), button, details, embed, iframe, img (if the usemap attribute is present), input (if the type attribute is not in the Hidden state), label, object (if the usemap attribute is present), select, textarea, video (if the controls attribute is present).

    [Interaktywna zawartość to zawartość, która jest przeznaczona do interakcji z użytkownikiem.

    a (jeśli jest obecny atrybut [href]), audio (jeśli jest obecny atrybut controls), button, details, embed, iframe, img (jeśli jest obecny atrybut [usemap]), input (jeśli pole nie jest [type=hidden]), label, object (jeśli jest obecny atrybut [usemap]), select, textarea, video (jeśli jest obecny atrybut [controls]).]

    Umieszczanie linków w przyciskach albo przycisków w linkach jest po prostu niepoprawne z punktu widzenia składni HTML-a.

  • Całkowicie psuje to nawigację klawiaturą. Jeśli na przykładowej stronie spróbuje się nawigować przy pomocy klawiatury, naciskając Tab, to szybko zauważy się, że na każdym przycisku istnieją dwa tab stopy. Tym samym każdy element trzeba przejechać dwukrotnie.

    Tego typu zachowanie jest jeszcze bardziej mylące dla użytkowników czytników ekranowych.

  • Jeśli już jesteśmy przy czytnikach ekranu, to zagnieżdżanie w sobie interaktywnych elementów może prowadzić do konsternacji. Użytkownik nie będzie wiedział, czemu strona ma przycisk „Kup teraz” oraz link „Kup teraz” i czemu jedno z nich wydaje się nie działać (lub czemu obydwa robią to samo).

    Zresztą ten problem można także odtworzyć na przykładowej stronie. Jeśli sfocusujemy link, w którym jest przycisk (trzeci tab stop), to wówczas po naciśnięciu Entera/spacji nic się nie stanie. Akcja jest bowiem przypisana dopiero do przycisku. Gdyby nasz link miał nieco większy padding, to można byłoby nawet powtórzyć to samo przy pomocy myszki.

Jaka jest zatem alternatywa? Po prostu ostylować link identycznie jak przycisk lub przycisk identycznie jak link. Tego typu rozwiązanie jest zdecydowanie mniej problematyczne niż wzorzec polegający na zagnieżdżaniu tych elementów. Niemniej wciąż wprowadza to niekonsystencję doświadczenia, sprawiając trudności m.in. w czasie używania programów do kontroli głosem (użytkownik widzi przycisk, więc instruuje program, żeby kliknął przycisk i nic się nie dzieje, bo ten przycisk to w rzeczywistości link). W idealnym świecie linki wyglądałyby jak linki a przyciski jak przyciski. W naszym świecie wydaje się, że a[role=button] to – na chwilę obecną – najlepsze, co mamy.

7 komentarzy do “Wpadki i wypadki #11”

  1. Witaj, czy warto się uczyć teraz typescript, gdy go transpiluje do tsc main.ts -t es2015 to praktycznie są bardzo małe różnice w kodzie, względem ES6 lub nowszych standardów. Dochodzi tylko dodatkowa robota i chaos w podwójnych plikach. Nie wiadomo też czy Deno utrzyma się na runku i zostanie konkurencją Node.

    1. Nie bardzo rozumiem, co oznacza chaos w podwójnych plikach?

      TypeScript to nie tylko Deno. Rzekłbym, że akurat Deno jest najmniej ważny. TypeScript to po prostu JS z dodanymi statycznymi typami, a to się przydaje przy większych projektach. Warto go znać – ale niekoniecznie uczyć. Jeśli zna się JS, to nauczenie się TS-a sprowadza się tak naprawdę do poznania sposobów na dodanie typów do naszego kodu.

  2. Ja ze stałym typowaniem nie mam problemów, ponieważ wpierw uczyłem się języka z takim typowaniem. Chodzi mi raczej o to jakie będę miał korzyści ucząc się tylko TS względem najnowszego ES12? Wydaje mi się że po pewnym czasie TS zniknie z rynku jak Dart i CoffeScript w frontendzie i najnowsze standardy JS wyeliminują ten dziwny twór Microsoftu. Nie podoba mi się w TypeScript takiego łączenia programowania obiektowego z klasami z programowaniem funkcyjnym. Wyszła z tego języka taka hybryda wymieszania funkcji i klas..

    1. Ale taki przecież jest też JS – ma zarówno składnię klas (chociaż pod spodem jest model obiektowości prototypowej), jak i pewne cechy języka funkcyjnego. To po prostu język wieloparadygmatowy.

      Nie bardzo widzę sens uczenia się „samego” TS-a, bo chcąc nie chcąc uczymy się JS-a.

      1. Dawno temu uczyłem się starego JS i był w pełni funkcyjny, nie było tam klas. Zależy mi na prostym języku do tworzenia zwykłych startupów. Natomiast C# i Java to nie moja liga, za dużo nauki i skomplikowania. Próbowałem zaprzyjaźnić się z Ruby, ale przy instalacji Ruby on Rails 6 co chwile wyskakiwały mi błędy przy instalacji webpacker i odpuściłem. Z nowszy technologii jest jeszcze Elixir, Crystal, Go.

      2. Z tego co wiem core PHP 7 przepisali w całości od podstaw dlatego tak przyspieszył. Natomiast JS i jego core czyli silnik V8 nadal jest stopniowo ulepszany, ale nie przepisywali go od podstaw jak w przypadku PHP? Twórcy PHP w wersji 7 wyeliminowali takie błędy jak 5 + '5′ = 55? Czy pozostała nadal kompatybilność wsteczna?

        1. Twórcy PHP w wersji 7 wyeliminowali takie błędy jak 5 + ‚5’ = 55?

          Hmm, trudno wyeliminować coś, co nie jest błędem, tylko wynika z zasad koercji typów w danym języku.

          Czy pozostała nadal kompatybilność wsteczna?

          W dużej mierze tak. Jeśli gdzieś nie jest, jest to wyraźnie zaznaczone w dokumentacji.

          Dawno temu uczyłem się starego JS i był w pełni funkcyjny, nie było tam klas.

          JS nigdy nie był w pełni funkcyjny. Poza tym dalej nie ma w nich klas jako takich, jest jedynie klasowy sposób zapisu prototypów.

Skomentuj coffeescript Anuluj pisanie odpowiedzi

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.