Ich habe diesen Artikel von Chris gelesen, in dem er über Block Links spricht – wissen Sie, wie man ein ganzes Karten-Element in einen Anker einbettet – als schlechte Idee. Es ist schlecht für die Barrierefreiheit, da es Bildschirmlesegeräte beeinträchtigt. Und es ist schlecht für die Benutzerfreundlichkeit, da es einfache Benutzeraufgaben wie das Auswählen von Text verhindert.
Aber vielleicht spielt etwas anderes eine Rolle. Vielleicht liegt das Problem weniger am Muster als an dessen Implementierung. Das hat mich zu der Überzeugung gebracht, dass jetzt der richtige Zeitpunkt ist, einen Folgeartikel zu schreiben, um zu sehen, ob wir einige der Probleme lösen können, auf die Chris hingewiesen hat.
In diesem Beitrag verwende ich den Begriff „Karte“, um eine Komponente zu beschreiben, die das Block-Link-Muster verwendet. Hier ist, was wir damit meinen.
Sehen wir uns an, wie unsere Karten-Komponenten funktionieren sollen
- Das Ganze sollte verlinkt und klickbar sein.
- Es sollte mehr als einen Link enthalten können.
- Der Inhalt sollte semantisch sein, damit assistive Technologien ihn verstehen können.
- Der Text sollte auswählbar sein, wie normale Links.
- Dinge wie Rechtsklick und Tastenkombinationen sollten damit funktionieren
- Seine Elemente sollten beim Tab-Wechsel fokussierbar sein.
Das ist eine lange Liste! Und da der Browser keine standardmäßige Karten-Widget bereitstellt, haben wir keine Standardrichtlinien für dessen Erstellung.
Wie bei den meisten Dingen im Web gibt es mehr als eine Möglichkeit, eine Kartenkomponente zu erstellen. Ich habe jedoch noch nichts gefunden, das alle von uns gerade behandelten Anforderungen erfüllt. In diesem Artikel werden wir versuchen, alle zu erfüllen. Das werden wir jetzt tun!
Methode 1: Alles in einen <a> einbetten
Dies ist die gängigste und einfachste Methode, eine verlinkte Karte zu erstellen. Nehmen Sie den HTML-Code für die Karte und betten Sie das Ganze in ein Anker-Tag ein.
<a href="/">
<!-- Card markup -->
</a>
Hier ist, was uns das bringt
- Sie ist klickbar.
- Sie funktioniert mit Rechtsklick und Tastenkombinationen.
Nun ja, nicht gut. Wir können immer noch nicht
- Einen weiteren Link in die Karte einfügen, da das Ganze ein einzelner Link ist
- Sie mit einem Bildschirmlesegerät verwenden – der Inhalt ist nicht semantisch, sodass assistive Technologien alles innerhalb der Karte ankündigen, beginnend mit dem Zeitstempel
- Text auswählen
Das ist genug 👎, dass wir es wahrscheinlich nicht benutzen sollten. Gehen wir zur nächsten Technik über.
Methode 2: Nur verlinken, was verlinkt werden muss
Dies ist ein guter Kompromiss, der etwas Benutzerfreundlichkeit für verbesserte Barrierefreiheit opfert.
Mit diesem Muster erreichen wir die meisten unserer Ziele
- Wir können so viele Links einfügen, wie wir möchten.
- Inhalt ist semantisch.
- Wir können den Text der Karte auswählen.
- Rechtsklick und Tastenkombinationen funktionieren.
- Der Fokus liegt beim Tabulatorwechsel in der richtigen Reihenfolge.
Aber es fehlt die Hauptfunktion, die wir uns von einer Karte wünschen: Das Ganze sollte klickbar sein! Sieht so aus, als müssten wir einen anderen Weg versuchen.
Methode 3: Das gute alte ::before Pseudo-Element
Bei dieser Methode fügen wir ein ::before oder ::after Element hinzu, platzieren es mit absoluter Positionierung über der Karte und dehnen es über die gesamte Breite und Höhe der Karte aus, damit es klickbar ist.
Aber jetzt
- Wir können immer noch nicht mehr als einen Link hinzufügen, da alles andere, was verlinkt ist, unter der Pseudo-Element-Ebene liegt. Wir können versuchen, den gesamten Text über das Pseudo-Element zu legen, aber der Kartenlink selbst funktioniert nicht, wenn man über den Text klickt.
- Wir können den Text immer noch nicht auswählen. Auch hier könnten wir die Ebenen tauschen, aber dann sind wir wieder beim Problem des klickbaren Links.
Versuchen wir, mit unserer letzten Technik wirklich alle Kriterien zu erfüllen.
Methode 4: Etwas JavaScript auf die zweite Methode streuen
Wir bauen auf der zweiten Methode auf. Erinnern Sie sich, das war die, bei der wir alles verlinken, was ein Link sein soll
<article class="card">
<time datetime="2020-03-20">Mar 20, 2020</time>
<h2><a href="https://css-tricks.de/a-complete-guide-to-calc-in-css/" class="main-link">A Complete Guide to calc() in CSS</a></h2>
<p>
In this guide, let’s cover just about everything there is to know about this very useful function.
</p>
<a class="author-name" href="https://css-tricks.de/author/chriscoyier/" target="_blank">Chris Coyier</a>
<div class="tags">
<a class="tag" href="https://css-tricks.de/tag/calc/" >calc</a>
</div>
</article>
Wie machen wir also die *ganze* Karte klickbar? Wir könnten JavaScript als progressive Verbesserung dafür nutzen. Wir beginnen damit, einen click Event-Listener zur Karte hinzuzufügen und den Klick auf den Hauptlink auszulösen, wenn er ausgelöst wird.
const card = document.querySelector(".card")
const mainLink = document.querySelector('.main-link')
card.addEventListener("click", handleClick)
function handleClick(event) {
mainLink.click();
}
Vorübergehend führt dies zu dem Problem, dass wir den Text nicht auswählen können, was wir die ganze Zeit versucht haben zu beheben. Hier ist der Trick: Wir verwenden die relativ unbekannte Web-API window.getSelection. Laut MDN
Die Methode
Window.getSelection()gibt einSelection-Objekt zurück, das den vom Benutzer ausgewählten Textbereich oder die aktuelle Position des Cursors darstellt.
Obwohl diese Methode ein Objekt zurückgibt, können wir es mit toString() in einen String konvertieren.
const isTextSelected = window.getSelection().toString()
Mit einer Zeile und ohne komplizierte Tricks mit Event-Listenern wissen wir, ob der Benutzer Text ausgewählt hat. Verwenden wir das in unserer handleClick Funktion.
const card = document.querySelector(".card")
const mainLink = document.querySelector('.main-link')
card.addEventListener("click", handleClick)
function handleClick(event) {
const isTextSelected = window.getSelection().toString();
if (!isTextSelected) {
mainLink.click();
}
}
Auf diese Weise kann der Hauptlink angeklickt werden, wenn kein Text ausgewählt ist, und das alles erforderte nur ein paar Zeilen JavaScript. Das erfüllt unsere Anforderungen
- Das Ganze ist verlinkt und klickbar.
- Es kann mehr als einen Link enthalten.
- Dieser Inhalt ist semantisch, damit assistive Technologien ihn verstehen können.
- Der Text sollte auswählbar sein, wie normale Links.
- Dinge wie Rechtsklick und Tastenkombinationen sollten damit funktionieren
- Seine Elemente sollten beim Tab-Wechsel fokussierbar sein.
Wir haben alle Anforderungen erfüllt, aber es gibt immer noch einige Haken, wie z. B. doppelte Event-Auslösung bei klickbaren Elementen wie Links und Schaltflächen in der Karte. Wir können dies beheben, indem wir auf allen von ihnen einen Klick-Event-Listener hinzufügen und die Weiterleitung des Events stoppen.
// You might want to add common class like 'clickable' on all elements and use that for the query selector.
const clickableElements = Array.from(card.querySelectorAll("a"));
clickableElements.forEach((ele) =>
ele.addEventListener("click", (e) => e.stopPropagation())
);
Hier ist die finale Demo mit dem gesamten JavaScript-Code, den wir hinzugefügt haben
Ich glaube, wir haben es geschafft! Jetzt wissen Sie, wie man eine perfekte klickbare Kartenkomponente erstellt.
Was ist mit anderen Mustern? Was ist zum Beispiel, wenn die Karte den Auszug eines Blogbeitrags enthält, gefolgt von einem „Weiterlesen“-Link? Wo soll dieser hin? Wird das zum „Haupt“-Link? Was ist mit dem Bild?
Für diese und weitere Fragen gibt es hier weiterführende Lektüre zum Thema
- Karten von Heydon Pickering
- Block Links, Karten, Klickbare Bereiche, Zeilen usw. von Adrian Roselli
- Block Links sind ein Problem (und vielleicht einfach eine schlechte Idee) von Chris Coyier
- Fallstricke von Karten-UIs von Dave Rupert
Sie möchten dies vielleicht in Firefox testen. In Firefox öffnen sich beim Klicken auf eine Schaltfläche oder einen Link innerhalb der Karte zwei neue Tabs (statt nur einem). Wenn ich zum Beispiel auf den „Mathe“-Button klicke, öffnen sich zwei verschiedene Seiten in neuen Tabs.
Ja, das kann ich gerade auch in Firefox bestätigen. Und Chrome öffnet dort den unerwarteten Link.
Wahrscheinlich muss man
event.targettesten und eine etwas andere Handhabung haben, wenn der Klick auf einen Link erfolgt, der keinmain-linkist.Man könnte beim Klickcheck
.contains()verwenden, um zu sehen, ob das angeklickte Element innerhalb des übergeordneten Knotens enthalten ist.Alternativ kann man rückwärts gehen und
.closest()verwenden, um vom Kindknoten aus zu prüfen, ob er innerhalb eines übergeordneten Knotens enthalten ist.Außerdem könnte man, um dies robuster zu gestalten, diese verwenden, um Tests einzuschließen, die sicherstellen, dass sich keine
onclick-,href- oder Formularelemente in der Kette befinden.Ich habe eine Lösung gefunden und werde den Blog bald aktualisieren. In der Zwischenzeit können Sie sich das Pen https://codepen.io/vikas-parashar/pen/qBOwMWj für die vorgeschlagene Lösung ansehen.
Ich habe mit Methode 3 herumgespielt und CSS pointer-events verwendet, um eine JavaScript-freie Lösung zu erhalten, die Sie weitgehend ans Ziel bringt. Das einzige, was fehlt, ist die Textauswahl. https://codepen.io/dillonbheadley/pen/BaogrGm
Durch die Verwendung von
display: gridkönnen Sie den z-index den Kindelementen hinzufügen, ohne das absolut positionierte Pseudo-Element zu beeinträchtigen. Dies ermöglicht es Ihnen, den Inhalt über den Pseudo-Link zu verschieben. Dann verhindert die Verwendung vonpointer-events: nonebei allem, was kein Anker ist, dass etwas den Klick überall auf der Karte blockiert.Ich würde argumentieren, dass Methode 3 mit einer kleinen Anpassung die beste Lösung ist. Verwenden Sie einfach z-index, um die zusätzlichen Links innerhalb der Karte nach vorne zu bringen. Kein JavaScript erforderlich, das der Benutzer möglicherweise sowieso blockiert, und es ist besser für die Leistung.
Wir können die Links mit z-index nach vorne bringen, aber wir können den Text immer noch nicht auswählen. Das ist jetzt je nach Anwendungsfall in Ordnung, bei dem Sie den Mangel daran nicht stören.
Wir können den Text durch Vorholen des Textes mit z-index auswählbar machen, aber dann gibt es Bereiche, in denen das Klicken nicht funktioniert.
Was die Verwendung von JS angeht, so funktioniert dies wie Methode 2, die gesamte Karte ist nicht klickbar, aber es ist eine progressive Verschlechterung, die meiner Meinung nach für einige Fälle, in denen JS deaktiviert ist, in Ordnung ist.
Richtig, Vikas, ich habe deine Anforderungsliste überflogen und die eine übersehen, bei der alles klickbar sein muss, aber der Text auch auswählbar sein muss. Ohne JavaScript-Magie, wie in deinem Beispiel, wirst du das nicht erreichen können. Normalerweise würde ich „alles muss klickbar sein“ und „Text muss auswählbar sein“ als Widersprüche betrachten, daher ist das ein interessanter Workaround. Ich kann mir ehrlich gesagt keinen Anwendungsfall dafür vorstellen. Wenn sich ein Benutzer besonders für den Inhalt auf der verlinkten Karte interessiert, sollte er meiner Meinung nach auf die Karte klicken und möglicherweise noch mehr Informationen erhalten, an denen er interessiert ist. Es ist schließlich eine kurze Zusammenfassung einer Seite mit Inhalten, zu deren Lesen er verleitet werden soll.
Ich habe genau dasselbe gedacht :)
Coole Demo! Mach das jetzt mal ohne JS
es funktioniert genauso wie Methode 2 mit deaktiviertem JS :)
Allerdings hat Methode 2 ihre Tücken, wie Chris erwähnt hat! Eine JS-freie Version der letzten Methode könnte eine lustige Herausforderung sein.
Für Methode #3 können Sie mit
z-indexspielen, um die anderen Links vor das Pseudo-Element zu legen, aber ja, immer noch kein auswählbarer Text.Das ist gut – wir hatten damit bei ABC zu kämpfen. Eine kleine Sache, die mir aufgefallen ist: Ich erhalte die URL-Vorschau in der unteren linken Ecke nicht, es sei denn, ich fahre direkt über den
<a>. Aber selbst dann ist das besser als unser Versuch!Ja, die URL-Vorschau ist beim Überfahren der tatsächlichen Links immer noch sichtbar und das ist einer der Kompromisse, die mit der finalen Lösung einhergehen, aber es wird bevorzugt, da es die Benutzerfreundlichkeit (Textauswahl, gesamte Karte klickbar) und die Barrierefreiheit verbessert.
Eines fehlt bei dieser Technik noch: Man fährt mit der Maus darüber, aber man sieht nicht, wohin man klickt (Browser zeigen unten links auf der Seite einen Tooltip an)
Ich weiß nicht, wie viele Benutzer diese Funktion nutzen, abgesehen von Entwicklern, aber trotzdem.
Ja, das ist einer der Kompromisse, die mit der finalen Lösung einhergehen.
Methode 3. Ja, wir können weitere Links hinzufügen.
Fügen Sie einfach hinzu
.author-name, .tag {
position: relative;
z-index: 2;
}
Ja, das können wir so machen.
Hier ist eine behobene Version, die auch in Firefox funktioniert
Schöne Lösung, aber sie funktioniert nicht, wenn der Link Kindelemente hat (wie Chris' Avatar). In diesem Fall werden wieder zwei Events ausgelöst. Ich habe eine Lösung gefunden und werde den Blog bald aktualisieren. In der Zwischenzeit können Sie sich das Pen https://codepen.io/vikas-parashar/pen/qBOwMWj für die vorgeschlagene Lösung ansehen.
Könnten Sie nicht einfach einen absolut positionierten Link innerhalb der Karte verwenden? Fügen Sie dort einen visuell versteckten Span ein, um ihn zugänglich zu halten. Da der Link absolut positioniert ist, funktionieren die anderen Links in der Karte weiterhin. Verwenden Sie z-index, um sie nach vorne zu bringen.
Meiner Meinung nach ist dies die beste Lösung, da Sie sich nicht mit JS herumschlagen müssen. Ich bin bei Websites, an denen ich gearbeitet habe, auf JS-basierte Lösungen für dieses Problem gestoßen, und es war immer mühsam zu aktualisieren, da JS die Fehlersuche erschwert, wenn Sie Änderungen vornehmen müssen usw.
Das berücksichtigt keine Textauswahl. Das mag für Sie völlig in Ordnung sein, aber der Punkt dieses Artikels ist, eine strenge Anforderung dafür aufzustellen, dass es „perfekt“ (oder so nah wie möglich) ist und dies anzustreben.
Super. Ich bin auch diesen Weg gegangen, als ich das letzte Mal so etwas bauen musste.
Aber es fehlen immer noch zwei Dinge, die meiner Meinung nach schlechter für die UX sind als Methode 2 allein
– URL-Vorschau (in anderen Kommentaren erwähnt)
– Öffnen in einem neuen Tab beim Klicken auf die gesamte Karte. Ich denke, es ist für den Benutzer nicht klar, warum er manchmal das richtige Kontextmenü und manchmal nicht erhält.
Ich bin sehr skeptisch gegenüber dem „Lass uns die ganze Karte verlinken“-Gedanken. Persönlich bevorzuge ich Methode 2. Aber darüber kann man stundenlang diskutieren ;)
Trotzdem ein schöner Artikel!
Andy Bell hat sich in diesem Artikel mit einigen ähnlichen Themen beschäftigt: https://hankchizljaw.com/wrote/create-a-semantic-breakout-button-to-make-an-entire-element-clickable/
Er geht jedoch davon aus, was eine gute Annahme zu sein scheint, dass in einer Kartenkomponente nur ein einziger Link vorhanden sein muss.
Ich verstehe dieses Verlangen nach auswählbarem Text auf Links nicht wirklich.
Wenn Sie einen normalen Link haben, können Sie den Teil davon nicht auswählen, wenn Sie Ihre Auswahl innerhalb dieses Links beginnen – Sie müssen davor oder danach beginnen, um ihn auswählen zu können. Hier funktioniert es genauso.
Dies ist ein klassisches Frontend-Dilemma, aber für mich scheint es eine seltsame UX-Anforderung zu sein, einen „zweiten“ Link innerhalb einer Karte zu haben, die vollständig klickbar sein soll.
Ich verstehe, warum es nützlich sein könnte, insbesondere für den Autorennamen in diesem Fall, aber ich denke, das läuft darauf hinaus, dass die gesamte Karte nicht klickbar sein sollte, wenn wir sekundäre Links in der Karte benötigen.
Wenn die Karte ein einziger Link ist, scheint es sinnvoll, einen Anker zum Einbetten zu verwenden, andernfalls scheint die Auszeichnung von inneren Links für jede Komponente (Titel, Bild, Autor) am sinnvollsten.
Sie können die Lösung der Apple-Website verwenden. Es ist ein div mit der Klasse .unit-wrapper und der Hauptanker mit der Klasse .unit-link. So erhält alles im Inneren (außer Anker) die Eigenschaft „pointer-events:none“.
// Ein Bereich wird in einen Link-Block verwandelt
// Dabei wird die Interaktion mit anderen Links im Inneren beibehalten
// fügen Sie der Hauptanker-Klasse .unit-link hinzu
a.unit-link {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 3;
}
// fügen Sie der übergeordneten El-Klasse .unit-wrapper hinzu
<
div>
.unit-wrapper {
position: relative;
overflow: hidden;
}
.unit-wrapper > div {
position: relative;
z-index: 4;
pointer-events: none;
}
.unit-wrapper a:not(.unit-link) {
z-index: 4;
pointer-events: all;
}
Super. Ich bin auch diesen Weg gegangen, als ich das letzte Mal so etwas bauen musste.
Aber es fehlen immer noch zwei Dinge, die meiner Meinung nach schlechter für die UX sind als Methode 2 allein
– URL-Vorschau (in anderen Kommentaren erwähnt)
– Öffnen in einem neuen Tab beim Klicken auf die gesamte Karte. Ich denke, es ist für den Benutzer nicht klar, warum er manchmal das richtige Kontextmenü und manchmal nicht erhält.
Ich bin sehr skeptisch gegenüber dem „Lass uns die ganze Karte verlinken“-Gedanken. Persönlich bevorzuge ich Methode 2. Aber darüber kann man stundenlang diskutieren ;)
Trotzdem ein schöner Artikel!
Und wurde dies mit mehreren Karten getestet? Nur die erste Karte im Stapel verhält sich wie vorgesehen. Die anderen Karten werden ignoriert – das bedeutet, die gesamte Karte ist nicht klickbar.
Dieses Problem ist mir auch aufgefallen. Muss für mehrere Karten funktionieren
Hallo Vikas,
Ich mochte Ihren Artikel sehr, Ihre Lösung scheint ein guter Mittelweg zu sein, der für die meisten Anwendungsfälle funktioniert.
Ich habe versucht, etwas Web Component-inspiriertes daraus zu machen (die Kapselung funktioniert dafür hervorragend)
https://webcomponents.dev/preview/dyflJofod72crJWUDyyo
Grundsätzlich ersetzt es den obersten Container und Sie können mit dem
main-link-Attribut festlegen, welchen Selektor Sie für den Hauptlink verwenden möchten.Der Code würde ungefähr so aussehen
Und da der gesamte Inhalt in einem Slot liegt, können Sie die Elemente so gestalten, wie Sie es normalerweise tun würden.
Es ist immer noch eine sehr einfache Implementierung. Wenn es Ihnen nichts ausmacht, könnte ich sie aufpolieren (hauptsächlich Dokumentation und A11y-bezogen) und auf npm veröffentlichen (natürlich mit angemessener Nennung von Ihnen und Ihrem Artikel).
Ja, nur zu! Vielleicht polieren Sie es etwas auf und fügen die anderen Links hinzu, die ich im Beitrag zum Nachschlagen verlinkt habe. Sie können diesen Artikel und meinen Twitter-Account (@vicode_in) in den Credits erwähnen, wenn das in Ordnung ist.
Sicher, ich werde die hier zitierten Artikel zusammen mit diesem hinzufügen
Ich werde Sie auf Twitter anpingen, sobald ich die Komponente etwas weiter aufpoliert habe
Wie bekommen Sie das mit mehr als einem Element zum Laufen? Wie Sie sehen können, ist nur der erste Artikel vollständig klickbar.
Ich habe die gleiche Frage.
@Leone, ich habe mich schließlich für die Implementierung von Sara Soueidan entschieden, die ziemlich gut funktioniert und kein JS benötigt. https://css-tricks.de/breakout-buttons/
Danke für die Info!
Ich habe die Implementierung von Sara Soueidan ausprobiert, aber aus irgendeinem Grund funktioniert sie auf Mobilgeräten nicht. Wenn ich auf die Karte klicke, wird der Hover-Effekt auf der Schaltfläche (die ich nicht angeklickt habe) ausgelöst, aber die Seite navigiert nicht zur URL.
Daher musste ich unser ursprüngliches Problem, dass nur die erste Karte funktionierte, mit etwas jQuery beheben
@Leone – Ihre Lösung in diesem Kommentar https://css-tricks.de/block-links-the-search-for-a-perfect-solution/#comment-1769257 hat für mich gut funktioniert.
Danke
Ich bin kein großer Fan davon, die ganze Karte klickbar zu machen, besonders wenn mehrere Links in der Karte verwendet werden. Würden Sie als Benutzer erwarten, dass das Klicken auf den Link „Chris Coyier“ zu einer URL führt und das Klicken knapp daneben zu einer anderen URL?