ساختن کارت شناور تعاملی

وقتی کاربر به پیوندی ابراز علاقه می‌کند (ازطریق نگه داشتن نشانگر یا تمرکز)، کارت شناور پیش‌نمای تعاملی از محتوا ارائه می‌دهد. این اطلاعات قبل‌از پیمایش به کاربران نشان داده می‌شود و محتوای موردنیاز را به کاربر ارائه می‌دهد یا کمک می‌کند تأیید شود که پیوند حاوی محتوای موردنظر کاربر است.

اگر به‌جای پیش‌نمایش‌های پیوند ساختاریافته به نکته‌ابزارهای غیرتعاملی با نوشتار ساده نیاز دارید، نمایش نکته‌ابزار هنگام نگه داشتن نشانگر را ببینید.

۱. درحال راه‌اندازی کارت اطلاعات

از میانای برنامه‌سازی کاربردی Interest Invokers برای راه‌اندازی کارت شناور استفاده کنید. این کار باعث می‌شود کارت هم هنگام نگه داشتن نشانگر روی آن و هم هنگام تمرکز روی آن ظاهر شود و سیم‌کشی دسترس‌پذیری را به‌طور خودکار مدیریت می‌کند.

  • مشخصه popover="hint" را به محفظه کارتتان اضافه کنید. از hint استفاده کنید تا منوهای بالاپری نامرتبطی که ممکن است باز باشند بسته نشوند.
  • مشخصه interestfor را به پیوند راه‌انداز اضافه کنید و آن را به id کارت ارجاع دهید.
<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>

نیازی نیست پیوند دیگری به محتوای داخل کارت شناور اضافه کنید، زیرا این کار تکرار پیوند راه‌انداز است.

دسترس‌پذیری در interestfor تعبیه شده است

وقتی از interestfor استفاده می‌کنید، مرورگر سیم‌کشی فناوری کمکی را برای شما مدیریت می‌کند:

  • معناشناسی ضمنی: هدف با popover="hint" حداقل نقش ضمنی tooltip را به‌دست می‌آورد. role="tooltip" را خودتان تنظیم نکنید.
  • ارتباط ضمنی: وقتی هدف نوشتار ساده باشد، مرورگر به‌طور ضمنی عنصر منبع را ازطریق aria-describedby با هدف مرتبط می‌کند، یا وقتی هدف حاوی محتوای تعاملی باشد، این کار را ازطریق aria-details انجام می‌دهد. aria-describedby یا aria-details را به راه‌انداز اضافه نکنید.
  • محتوای تعاملی: چون درصورت نیاز، ارتباط به aria-details تغییر می‌کند، هدف می‌تواند محتوای تعاملی داشته باشد (مثلاً پیوند درون «کارت علاقه»).

۲. موقعیت‌یابی با «موقعیت‌یابی لنگر»

بااستفاده از موقعیت‌یابی لنگر CSS، کارت شناور را نسبت به راه‌انداز آن قرار دهید. هنگام استفاده از interestfor، راه‌انداز به لنگر ضمنی برای بالاپریدنی تبدیل می‌شود.

[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;
}

۳. پویانمایی ورود و خروج

به‌طور پیش‌فرض، علاقه برای کاربران صفحه‌کلید و موشواره با تأخیر نیم ثانیه‌ای نشان داده می‌شود و ازبین می‌رود. این کار از باز شدن پاپ‌اور هدف هنگام جابه‌جایی بین زبانه‌های صفحه یا حرکت دادن موشواره و از بسته شدن آن هنگام حرکت دادن موشواره از راه‌انداز به پاپ‌اور جلوگیری می‌کند. برای تنظیم زمان‌بندی، از خصوصیت CSS interest-delay استفاده کنید.

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

اگر کاربران احتمالاً به‌سرعت از چندین راه‌انداز عبور می‌کنند، درصورتی‌که یک بالاپَر راه‌اندازی‌شده براساس علاقه ازقبل باز است، تأخیر اولیه را روی 0s تنظیم کنید.

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

از @starting-style برای تعریف پویانمایی ورودی و از transition-behavior: allow-discrete (همراه با display و overlay) برای پویانمایی خروج از لایه بالایی استفاده کنید و گذارها را پشت @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;
  }
}

۴. اختیاری: تبدیل یکپارچه به صفحه بعدی

می‌توانید به‌طور اختیاری از Cross-Document View Transitions برای ایجاد پویانمایی «دگردیسی» یکپارچه بین کارت شناور و صفحه مقصد استفاده کنید. برای راهنمایی درباره پیاده‌سازی، انتقال‌های بین‌سندی سازگار را ببینید.

با اختصاص دادن مقادیر view-transition-name منطبق به عناصر موجود در کارت شناور و همتایان آن‌ها در صفحه بعدی، مرورگر آن‌ها را در سراسر پیمایش پویانمایی می‌کند. می‌توانید چندین عنصر (مثلاً عنوان، تصویر، نام نویسنده) را نام ببرید تا جلوه دگردیسی پیچیده و چندعنصری ایجاد کنید. بااستفاده از :popover-open، view-transition-name را به عناصر موجود در منوِ بازشونده باز اختصاص دهید تا از ایجاد view-transition-name تکراری یا نیاز به view-transition-name مختص پیوند جلوگیری شود.

/* 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;
  }
}

اجباری: برای اطمینان از اینکه انتقال پایدار است و سوسو نمی‌زند، صفحه مقصد باید از blocking="render" و <link rel="expect"> استفاده کند تا قبل‌از رندر کردن قاب اول، منتظر تجزیه محتوای حیاتی بماند.

<!-- 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;
}

راهبردهای جایگزین

چون راه‌انداز یک پیوند استاندارد <a href="..."> است، مرورگرهای بدون پشتیبانی همچنان می‌توانند با کلیک کردن پیمایش کنند، اگر انتخاب کنید که با کارت شناور به‌عنوان ارتقای پیش‌رو رفتار شود. وقتی پشتیبانی از کارت شناور بین‌مرورگری لازم است، شبیه‌سازهای مرورگر را به‌صورت شرطی و درصورت نیاز بار کنید.

پشتیبانی مرورگر و بازگشت به حالت قبلی برای Popover

«میانای برنامه‌سازی کاربردی Popover» عمدتاً ارتقای پیش‌رو است، اما رفتارهای تعریف‌کننده آن (ارتقای لایه بالایی، رد کردن با کلیک در خارج از عنصر، و فراخوانی popovertarget) معادل CSS-only ندارند. مرورگرهای قدیمی‌تر به شبیه‌ساز مرورگر نیاز دارند، یا اگر ترجیح می‌دهید شبیه‌ساز مرورگر ارسال نکنید، به بازگشت دستی نیاز دارند.

شبیه‌ساز مرورگر: برای پشتیبانی از مشخصه popover در مرورگرهای قدیمی‌تر، @oddbird/popover-polyfill را به‌صورت شرطی بار کنید. اجباری: ویژگی را با بررسی کردن ویژگی popover در HTMLElement.prototype شناسایی کنید و شبیه‌ساز مرورگر را فقط زمانی بار کنید که پشتیبانی بومی وجود ندارد — آن را بدون قیدوشرط بار نکنید.

با بسته‌بندی یا نقشه واردات:

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

بدون بسته‌بندی، از CDN داخل <script type="module"> وارد کنید:

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

هشدار سبک‌بندی: شبیه‌ساز مرورگر نمی‌تواند کلاس کاذب واقعی :popover-open را تعریف کند، بنابراین به‌جای آن کلاس .\:popover-open را اعمال می‌کند. اجباری: این دو را با :is() یا :where() ترکیب کنید، درغیراین‌صورت مرورگرهایی که فاقد :popover-open هستند کل قانون را نادیده می‌گیرند:

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

یا برای بازگشت به نسخه قدیمی بدون شبیه‌ساز مرورگر، از position: fixed استفاده کنید و مختصات را به‌صورت دستی ازطریق getBoundingClientRect() محاسبه کنید یا اگر برای مورد استفاده قابل‌قبول است، به موقعیت‌یابی پیش‌فرض با inset: auto تکیه کنید.

پشتیبانی مرورگر و بازگشت به حالت قبل برای فراخوان‌های Interest

فراخوان‌های Interest باید به‌صورت مشروط بااستفاده از بسته شبیه‌ساز مرورگر interestfor از NPM شبیه‌سازی شوند. ترجیحاً شبیه‌ساز مرورگر را به‌جای استفاده از CDN بسته‌بندی کنید.

اجباری: ویژگی را با بررسی کردن ویژگی interestForElement در HTMLButtonElement.prototype شناسایی کنید و شبیه‌ساز مرورگر را فقط زمانی بار کنید که پشتیبانی بومی وجود ندارد.

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

هشدارهای شبیه‌ساز مرورگر CSS

هنگام استفاده از شبیه‌ساز مرورگر، CSS API کمی تغییر می‌کند تا عدم پشتیبانی از کلاس کاذب و ویژگی بومی را جبران کند:

  • شبه‌کلاس‌ها: شبیه‌ساز مرورگر نمی‌تواند شبه‌کلاس‌های واقعی :interest-source و :interest-target را تعریف کند، بنابراین به‌جای آن‌ها کلاس‌های .interest-source و .interest-target را اعمال می‌کند. اجباری: آن‌ها را با :is() ترکیب کنید تا از هر دو حالت بومی و شبیه‌سازی‌شده پشتیبانی شود.
/* Styles to apply when the effect is being previewed */
:is(:interest-source, .interest-source) { /* ... */ }
:is(:interest-target, .interest-target) { /* ... */ }
  • ویژگی‌های CSS: برای interest-delay، interest-delay-start، و interest-delay-end، شبیه‌ساز مرورگر به استفاده از متغیرهای CSS نیاز دارد زیرا نمی‌تواند ویژگی‌های سفارشی را مستقیماً در همه مرورگرها بخواند.
/* 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);
}