สร้างโฮเวอร์การ์ดแบบอินเทอร์แอกทีฟ

โฮเวอร์การ์ดจะแสดงตัวอย่างเนื้อหาแบบอินเทอร์แอกทีฟเมื่อผู้ใช้แสดงความสนใจในลิงก์ (ผ่านการวางเมาส์เหนือหรือโฟกัส) ซึ่งจะแสดงต่อผู้ใช้ก่อนที่จะไปยังส่วนต่างๆ และให้เนื้อหาที่ผู้ใช้ต้องการหรือช่วยยืนยันว่าลิงก์จะมีเนื้อหาที่ผู้ใช้กำลังมองหา

หากต้องการเคล็ดลับเครื่องมือแบบข้อความธรรมดาที่ไม่มีการโต้ตอบแทนตัวอย่างลิงก์ที่มีโครงสร้าง โปรดดูหัวข้อแสดงเคล็ดลับเครื่องมือเมื่อวางเมาส์เหนือ

1. การทริกเกอร์โฮเวอร์การ์ด

ใช้ API 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 เมื่อจำเป็น เป้าหมายจึงได้รับอนุญาตให้มีเนื้อหาแบบอินเทอร์แอกทีฟ (เช่น ลิงก์ภายใน "การ์ดความสนใจ")

2. การจัดตำแหน่งด้วยการจัดตำแหน่ง Anchor

จัดตำแหน่งโฮเวอร์การ์ดให้สัมพันธ์กับทริกเกอร์โดยใช้การจัดตำแหน่ง Anchor ของ CSS เมื่อใช้ interestfor ทริกเกอร์จะกลายเป็น Anchor โดยนัยสำหรับป๊อปโอเวอร์

[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. การสร้างภาพเคลื่อนไหวสำหรับการเข้าและออกฉาก

โดยค่าเริ่มต้น ระบบจะแสดงและยกเลิกการแสดงความสนใจสำหรับผู้ใช้แป้นพิมพ์และเมาส์โดยมีความล่าช้าครึ่งวินาที ซึ่งจะป้องกันไม่ให้ป๊อปโอเวอร์เป้าหมายเปิดขึ้นขณะกด Tab เพื่อไปยังส่วนต่างๆ ของหน้าหรือเลื่อนเมาส์ไปมา และป้องกันไม่ให้ปิดขณะเลื่อนเมาส์จากทริกเกอร์ไปยังป๊อปโอเวอร์ หากต้องการปรับเวลา ให้ใช้พร็อพเพอร์ตี้ 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;
  }
}

4. ไม่บังคับ: การเปลี่ยนรูปแบบไปยังหน้าถัดไปอย่างราบรื่น

คุณสามารถเลือกใช้การเปลี่ยนมุมมองข้ามเอกสารเพื่อสร้างภาพเคลื่อนไหว "มอร์ฟ" ที่ราบรื่นระหว่างโฮเวอร์การ์ดกับหน้าปลายทางได้ ดูคำแนะนำในการติดตั้งใช้งานได้ที่การเปลี่ยนข้ามเอกสารที่สอดคล้องกัน

เมื่อกำหนดค่า view-transition-name ที่ตรงกันให้กับองค์ประกอบในโฮเวอร์การ์ดและองค์ประกอบที่ตรงกันในหน้าถัดไป เบราว์เซอร์จะแสดงภาพเคลื่อนไหวขององค์ประกอบเหล่านั้นในการไปยังส่วนต่างๆ คุณสามารถตั้งชื่อองค์ประกอบหลายรายการ (เช่น ชื่อ รูปภาพ ข้อมูลผู้เขียน) เพื่อสร้างเอฟเฟกต์การเปลี่ยนรูปแบบที่ซับซ้อนและมีองค์ประกอบหลายรายการได้ กำหนด view-transition-name ให้กับองค์ประกอบในป๊อปโอเวอร์ที่เปิดอยู่โดยใช้ :popover-open เพื่อหลีกเลี่ยงการสร้าง 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="..."> มาตรฐาน เบราว์เซอร์ที่ไม่รองรับจึงยังคงไปยังส่วนต่างๆ ได้เมื่อคลิก หากคุณเลือกที่จะถือว่าโฮเวอร์การ์ดเป็นการเพิ่มประสิทธิภาพแบบต่อเนื่อง เมื่อจำเป็นต้องรองรับโฮเวอร์การ์ดข้ามเบราว์เซอร์ ให้โหลด Polyfill แบบมีเงื่อนไขตามความจำเป็น

การรองรับเบราว์เซอร์และทางเลือกสำรองสำหรับ Popover

Popover API ส่วนใหญ่เป็นการเพิ่มประสิทธิภาพแบบต่อเนื่อง แต่ลักษณะการทำงานที่กำหนดไว้ เช่น การโปรโมตเลเยอร์บนสุด การปิดเมื่อคลิกนอกองค์ประกอบ และการเรียกใช้ popovertarget ไม่มี CSS ที่เทียบเท่า เบราว์เซอร์รุ่นเก่าต้องใช้ Polyfill หรือการสำรองข้อมูลด้วยตนเองหากคุณไม่ต้องการจัดส่ง

Polyfill: หากต้องการรองรับแอตทริบิวต์ popover ในเบราว์เซอร์รุ่นเก่า ให้โหลด @oddbird/popover-polyfill แบบมีเงื่อนไข จำเป็น: ตรวจหาฟีเจอร์โดยการตรวจสอบพร็อพเพอร์ตี้ popover ใน HTMLElement.prototype และโหลด Polyfill เฉพาะเมื่อไม่มีการรองรับแบบเนทีฟ อย่าโหลดโดยไม่มีเงื่อนไข

เมื่อใช้ Bundler หรือแผนที่การนำเข้า ให้ทำดังนี้

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

หากไม่มี Bundler ให้นำเข้าจาก 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>

ข้อควรระวังเกี่ยวกับรูปแบบ: Polyfill ไม่สามารถกำหนดคลาสจำลอง :popover-open จริงได้ จึงใช้คลาส .\:popover-open แทน จำเป็น: รวมทั้ง 2 รายการเข้าด้วยกันโดยใช้ :is() หรือ :where() ไม่เช่นนั้นเบราว์เซอร์ที่ไม่มี :popover-open จะทิ้งกฎทั้งหมด

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

หรือสำหรับทางเลือกสำรองแบบเดิมที่ไม่มี Polyfill ให้ใช้ position: fixed และคำนวณพิกัดด้วยตนเองผ่าน getBoundingClientRect() หรือใช้การกำหนดตำแหน่งเริ่มต้นด้วย inset: auto หากยอมรับได้สำหรับกรณีการใช้งาน

การสำรองและการรองรับเบราว์เซอร์สำหรับ Interest Invoker

Interest Invoker ต้องใช้ Polyfill แบบมีเงื่อนไขโดยใช้แพ็กเกจ Polyfill interestfor จาก NPM แนะนำให้รวม Polyfill แทนการใช้ CDN

บังคับ: ตรวจหาฟีเจอร์โดยตรวจสอบพร็อพเพอร์ตี้ interestForElement ใน HTMLButtonElement.prototype และโหลด Polyfill เฉพาะเมื่อไม่มีการรองรับแบบเนทีฟ

<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 Polyfill

เมื่อใช้ Polyfill, CSS API จะเปลี่ยนแปลงเล็กน้อยเพื่อรองรับการขาดการสนับสนุนคลาสเทียมและพร็อพเพอร์ตี้แบบเนทีฟ

  • Pseudo-class: Polyfill ไม่สามารถกำหนด Pseudo-class :interest-source และ :interest-target จริงได้ จึงใช้คลาส .interest-source และ .interest-target แทน บังคับ: รวมเข้ากับ :is() เพื่อรองรับทั้งสถานะเนทีฟและ Polyfill
/* 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 Polyfill ต้องใช้ตัวแปร 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);
}