Tworzenie interaktywnej karty informacyjnej

Karty informacyjne wyświetlają interaktywny podgląd treści, gdy użytkownik wyrazi zainteresowanie linkiem (przez najechanie kursorem lub zaznaczenie). Są one wyświetlane użytkownikom, zanim przejdą do strony, i zawierają potrzebne im treści lub pomagają sprawdzić, czy link prowadzi do treści, których szukają.

Jeśli potrzebujesz zwykłych, nieinteraktywnych etykietek, a nie ustrukturyzowanych podglądów linków, zapoznaj się z artykułem Wyświetlanie etykietki po najechaniu kursorem.

1. Wyświetlanie karty informacyjnej

Aby wywołać kartę informacyjną, użyj interfejsu API Interest Invokers. Dzięki temu karta będzie się wyświetlać zarówno po najechaniu kursorem, jak i po zaznaczeniu, a także automatycznie obsłuży ułatwienia dostępu.

  • Dodaj atrybut popover="hint" do kontenera karty. Użyj hint, aby uniknąć zamykania niepowiązanych menu wyskakujących, które mogą być otwarte.
  • Dodaj atrybut interestfor do linku aktywującego, wskazując id karty.
<a href="/article" interestfor="article-preview">Article title</a>

<!-- The `id` must match the `interestfor` value on the trigger. -->
<div id="article-preview" popover="hint" class="hovercard">
  <h2>The buzz on hummingbirds</h2>
  <p class="byline">By Delphi Aguilar</p>
  <div>
    <img src="bird.jpg" alt="A vibrant hummingbird">
    <p>Brief summary of the article...</p>
  </div>
</div>

Nie musisz dodawać dodatkowego linku do treści w karcie informacyjnej, ponieważ jest on zbędny w przypadku linku aktywującego.

Ułatwienia dostępu wbudowane w usługę interestfor

Gdy używasz elementu interestfor, przeglądarka obsługuje połączenie z technologią wspomagającą osoby z niepełnosprawnością:

  • Semantyka niejawna: element docelowy z atrybutem popover="hint" uzyskuje niejawną minimalną rolę tooltip. NIE ustawiaj samodzielnie parametru role="tooltip".
  • Powiązanie domyślne: przeglądarka domyślnie kojarzy element źródłowy z elementem docelowym za pomocą aria-describedby, gdy element docelowy jest zwykłym tekstem, lub za pomocą aria-details, gdy element docelowy zawiera treści interaktywne. NIE dodawaj do reguły aria-describedby ani aria-details.
  • Treści interaktywne: ponieważ powiązanie przełącza się na aria-details, gdy jest to konieczne, element docelowy MOŻE zawierać treści interaktywne (np. link w „karcie zainteresowań”).

2. Pozycjonowanie za pomocą pozycjonowania zakotwiczonego

Ustaw pozycję karty informacyjnej względem jej aktywatora za pomocą pozycjonowania kotwicy CSS. Gdy używasz interestfor, aktywator staje się niejawną kotwicą wyskakującego okienka.

[popover].hovercard {
  /* Position the card below the trigger, aligned with its inline-start edge
     and spanning toward the inline-end, flipping if it overflows the viewport. */
  position-area: block-end span-inline-end;
  position-try-fallbacks: flip-block, flip-inline;
}

3. Animowanie wejścia i wyjścia

Domyślnie zainteresowanie jest wyświetlane i tracone w przypadku użytkowników klawiatury i myszy z półsekundowym opóźnieniem. Zapobiega to otwieraniu docelowego wyskakującego okienka podczas przechodzenia między kartami na stronie lub poruszania myszą oraz zamykaniu go podczas przesuwania myszy z elementu wyzwalającego do wyskakującego okienka. Aby dostosować czas, użyj właściwości CSS interest-delay.

[interestfor]{
  /* Shorthand for interest-delay-start and interest-delay-end */
  interest-delay: 0.2s 0.1s;
}

Jeśli użytkownicy mogą szybko przechodzić przez kilka reguł, ustaw opóźnienie początkowe na 0s, jeśli wyskakujące okienko wywołane przez zainteresowanie jest już otwarte.

:root:has(:interest-source) [interestfor] {
  interest-delay-start: 0s;
}

Użyj @starting-style, aby zdefiniować animację wejścia, i transition-behavior: allow-discrete (wraz z display i overlay), aby animować wyjście z górnej warstwy, blokując przejścia za pomocą @media (prefers-reduced-motion: no-preference).

[popover].hovercard {
  opacity: 0;
  transform-origin: top;
}

[popover].hovercard:popover-open {
  opacity: 1;
  transform: translateY(0) scale(1);
}

@starting-style {
  [popover].hovercard:popover-open {
    opacity: 0;
    transform: translateY(-10px) scale(0.95);
  }
}

/* MANDATORY: Respect user preference for reduced motion by selectively applying transitions. */
@media (prefers-reduced-motion: no-preference) {
  [popover].hovercard {
    transition:
      opacity,
      transform,
      display,
      overlay;
    transition-duration: 0.4s;
    transition-behavior: allow-discrete;
  }
}

4. OPCJONALNIE: płynne przejście do następnej strony

Możesz też użyć przejść między widokami w różnych dokumentach, aby utworzyć płynną animację „morfowania” między kartą informacyjną a stroną docelową. Wskazówki dotyczące implementacji znajdziesz w artykule Spójne przejścia między dokumentami.

Jeśli przypiszesz pasujące wartości view-transition-name do elementów na karcie informacyjnej i ich odpowiedników na następnej stronie, przeglądarka będzie je animować podczas nawigacji. Możesz nazwać wiele elementów (np. tytuły, obrazy, podpisy), aby utworzyć złożony, wieloelementowy efekt przekształcania. Przypisz view-transition-name do elementów w otwartym wyskakującym okienku za pomocą :popover-open, aby uniknąć tworzenia duplikatów view-transition-name lub konieczności stosowania view-transition-name dla konkretnych linków.

/* Opt-in to cross-document transitions on both pages */
@view-transition {
  navigation: auto;
}

/* Don't show transitions when loading this page */
::view-transition-group(*) {
  animation-duration: 0s;
}

/* Assign names ONLY when the popover is open to ensure only the correct elements are selected. */
[popover]:popover-open {
  view-transition-name: --hovercard;
  view-transition-class: morph;

  h2 {
    view-transition-name: --title;
    view-transition-class: morph;
  }

  img {
    view-transition-name: --image;
    view-transition-class: morph;
  }
}

OBOWIĄZKOWE: aby przejście było stabilne i nie migotało, strona docelowa powinna używać blocking="render" i <link rel="expect">, aby poczekać na przeanalizowanie kluczowych treści przed wyrenderowaniem pierwszej klatki.

<!-- On the destination page: -->
<head>
  <!-- Block rendering until the hero image and title are parsed,
       ensuring the view transition snapshot is taken at the right time. -->
  <link rel="expect" href="#main-content" blocking="render">
</head>
<body>
  <main id="main-content">
    <h1>The buzz on hummingbirds</h1>
    <img src="bird.jpg">
  </main>
</body>
/* On the destination page: */
main {
  view-transition-name: --hovercard;
  view-transition-class: morph;
}
h1 {
  view-transition-name: --title;
  view-transition-class: morph;
}
img {
  view-transition-name: --image;
  view-transition-class: morph;
}

/* By default no transition */
::view-transition-group(*) {
  animation-duration: 0s;
}
/* Short transition for morphing elements */
::view-transition-group(.morph) {
  animation-duration: 0.4s;
}
/* Disable transition if no `old` view */
::view-transition-new(.morph):only-child {
  animation-duration: 0s;
}

Strategie zastępcze

Ponieważ aktywator jest standardowym linkiem <a href="...">, przeglądarki bez obsługi mogą nadal nawigować po kliknięciu, jeśli zdecydujesz się traktować kartę informacyjną jako progresywne ulepszanie. Jeśli wymagana jest obsługa kart informacyjnych w różnych przeglądarkach, w razie potrzeby wczytuj warunkowo polyfille.

Kreacje zastępcze i obsługa wyskakujących okienek w przeglądarkach

Interfejs Popover API to głównie progresywne ulepszanie, ale jego definiujące zachowania – promowanie warstwy górnej, lekkie odrzucanie i wywoływanie popovertarget – nie mają odpowiednika tylko w CSS. Starsze przeglądarki wymagają polyfilla lub ręcznego rozwiązania zastępczego, jeśli nie chcesz go dostarczać.

Polyfill: aby obsługiwać atrybut popover w starszych przeglądarkach, warunkowo wczytuj @oddbird/popover-polyfill. OBOWIĄZKOWO: wykryj funkcję, sprawdzając właściwość popover w HTMLElement.prototype, i wczytaj polyfill tylko wtedy, gdy brakuje obsługi natywnej – NIE wczytuj go bezwarunkowo.

Za pomocą bundlera lub mapy importu:

// MANDATORY: Feature detect 'popover' on HTMLElement.prototype.
if (!("popover" in HTMLElement.prototype)) {
  import("@oddbird/popover-polyfill");
}

Bez bundlera, importuj z sieci CDN w tagu <script type="module">:

<script type="module">
  if (!("popover" in HTMLElement.prototype)) {
    import("https://unpkg.com/@oddbird/popover-polyfill@latest/dist/popover.min.js");
  }
</script>

Uwaga dotycząca stylów: polyfill nie może zdefiniować rzeczywistej pseudoklasy :popover-open, więc zamiast tego stosuje klasę .\:popover-open. OBOWIĄZKOWO: połącz te 2 właściwości za pomocą :is() lub :where(), w przeciwnym razie przeglądarki, które nie obsługują :popover-open, odrzucą całą regułę:

[popover]:is(:popover-open, .\:popover-open) {
  display: block;
}

Możesz też użyć position: fixed i ręcznie obliczyć współrzędne za pomocą getBoundingClientRect() lub polegać na domyślnym pozycjonowaniu za pomocą inset: auto, jeśli jest to akceptowalne w danym przypadku użycia.

Zastępcze i obsługiwane przez przeglądarki wywołania zainteresowań

Elementy wywołujące zainteresowanie muszą być warunkowo wypełniane za pomocą pakietu polyfill interestfor z NPM. Zamiast korzystać z CDN, lepiej dołączyć polyfill do pakietu.

OBOWIĄZKOWO: wykryj funkcję, sprawdzając właściwość interestForElement w HTMLButtonElement.prototype, i wczytaj polyfill tylko wtedy, gdy brakuje obsługi natywnej.

<script type="module">
  if (!HTMLButtonElement.prototype.hasOwnProperty("interestForElement")) {
    // CDN link only used for example, prefer bundling.
    await import("https://unpkg.com/interestfor@latest");
  }
</script>

Zastrzeżenia dotyczące kodu polyfill CSS

W przypadku korzystania z kodu polyfill interfejs CSS API zmienia się nieznacznie, aby uwzględnić brak obsługi natywnej pseudoklasy i właściwości:

  • Pseudoklasy: polyfill nie może definiować rzeczywistych pseudoklas :interest-source i :interest-target, więc zamiast nich stosuje klasy .interest-source i .interest-target. OBOWIĄZKOWO: połącz je z :is(), aby obsługiwać zarówno stany natywne, jak i stany polyfill.
/* Styles to apply when the effect is being previewed */
:is(:interest-source, .interest-source) { /* ... */ }
:is(:interest-target, .interest-target) { /* ... */ }
  • Właściwości CSS: w przypadku interest-delay, interest-delay-start i interest-delay-end polyfill wymaga użycia zmiennych CSS, ponieważ nie może odczytać właściwości niestandardowych bezpośrednio we wszystkich przeglądarkach.
/* Adjust the start and end delay for interest invokers */
[interestfor] {
  --interest-delay-start: 0.2s;
  interest-delay-start: var(--interest-delay-start);
  --interest-delay-end: 0.1s;
  interest-delay-end: var(--interest-delay-end);
}