Membuat komponen Tab

Ringkasan dasar tentang cara membuat komponen tab yang mirip dengan yang ada di aplikasi iOS dan Android.

Dalam postingan ini, saya ingin berbagi pemikiran tentang pembuatan komponen Tab untuk web yang responsif, mendukung banyak input perangkat, dan berfungsi di berbagai browser. Coba demo.

Demo

Jika Anda lebih suka menonton video, berikut versi YouTube untuk postingan ini:

Ringkasan

{i>Tab <i}adalah komponen umum dari sistem desain tetapi bisa memiliki banyak bentuk dan formulir. Pertama, ada tab desktop yang dibuat berdasarkan elemen <frame>, dan sekarang kita punya komponen seluler yang menarik yang menganimasikan konten berdasarkan properti fisika. Mereka semua mencoba melakukan hal yang sama: menghemat ruang.

Saat ini, hal-hal penting dari pengalaman pengguna tab adalah area navigasi tombol yang mengubah visibilitas konten di {i>display frame<i}. Banyak area konten memiliki ruang yang sama, tetapi disajikan bersyarat berdasarkan yang dipilih di navigasi.

kolase ini cukup kacau karena banyaknya variasi gaya yang diterapkan web pada konsep komponen
Kolase gaya desain web komponen tab dari 10 tahun terakhir

Taktik Web

Secara keseluruhan, saya menemukan bahwa komponen ini cukup mudah dibuat, berkat beberapa fitur platform web penting:

  • scroll-snap-points untuk interaksi geser dan keyboard yang elegan dengan posisi penghentian scroll yang sesuai
  • Deep link melalui hash URL untuk browser menangani anchor scroll dalam halaman dan dukungan berbagi
  • Dukungan pembaca layar dengan markup elemen <a> dan id="#hash"
  • prefers-reduced-motion untuk mengaktifkan transisi crossfade dan instan scroll dalam halaman
  • Fitur web @scroll-timeline dalam draf untuk memberi garis bawah secara dinamis dan warna mengubah tab yang dipilih

HTML

Pada dasarnya, UX di sini adalah: klik tautan, buat URL mewakili status halaman, lalu lihat area konten diperbarui saat browser men-scroll ke yang sesuai.

Ada beberapa anggota konten struktural di sana: link dan :target. Rab memerlukan daftar link, yang cocok untuk <nav>, dan daftar <article> elemen yang sangat cocok dengan <section>. Setiap {i>hash<i} tautan akan cocok dengan satu bagian, memungkinkan browser men-scroll berbagai hal melalui anchoring.

Tombol link diklik, bergeser dalam konten yang difokuskan

Misalnya, mengklik link akan otomatis memfokuskan artikel :target di Chrome 89, tanpa JS. Pengguna kemudian dapat men-scroll konten artikel dengan perangkat input mereka seperti biasa. Konten ini bersifat pelengkap, seperti yang ditunjukkan dalam markup.

Saya menggunakan markup berikut untuk mengatur tab:

<snap-tabs>
  <header>
    <nav>
      <a></a>
      <a></a>
      <a></a>
      <a></a>
    </nav>
  </header>
  <section>
    <article></article>
    <article></article>
    <article></article>
    <article></article>
  </section>
</snap-tabs>

Saya dapat membuat koneksi antara elemen <a> dan <article> dengan Properti href dan id seperti ini:

<snap-tabs>
  <header>
    <nav>
      <a href="#responsive"></a>
      <a href="#accessible"></a>
      <a href="#overscroll"></a>
      <a href="#more"></a>
    </nav>
  </header>
  <section>
    <article id="responsive"></article>
    <article id="accessible"></article>
    <article id="overscroll"></article>
    <article id="more"></article>
  </section>
</snap-tabs>

Selanjutnya saya mengisi artikel dengan jumlah lorem yang beragam, dan tautannya dengan kombinasi panjang dan kumpulan gambar. Dengan konten yang akan dikerjakan, kita bisa mulai tata letak.

Tata letak scroll

Ada 3 jenis area scroll yang berbeda dalam komponen ini:

  • Navigasi (pink) secara horizontal dapat di-scroll
  • Area konten (biru) berada secara horizontal dapat di-scroll
  • Setiap item artikel (hijau) secara vertikal dapat di-scroll.
3 kotak warna-warni dengan panah penunjuk arah yang cocok dengan warna yang menguraikan area scroll dan menampilkan arah scroll.

Ada 2 jenis elemen berbeda yang terlibat dalam scroll:

  1. Jendela
    Kotak dengan dimensi yang ditentukan yang memiliki overflow gaya properti.
  2. Platform besar
    Dalam tata letak ini, yang dimaksud adalah penampung daftar: navigasi tautan, artikel rubrik, dan konten artikel.

Tata letak <snap-tabs>

Tata letak tingkat teratas yang saya pilih adalah {i>flex<i} (Flexbox). saya menetapkan arah ke column, sehingga header dan bagian diurutkan secara vertikal. Ini adalah men-scroll, dan menyembunyikan semua yang overflow. {i>Header<i} dan akan segera menerapkan overscroll, sebagai zona individual.

HTML
<snap-tabs>
  <header></header>
  <section></section>
</snap-tabs>
CSS
  snap-tabs {
  display: flex;
  flex-direction: column;

  /* establish primary containing box */
  overflow: hidden;
  position: relative;

  & > section {
    /* be pushy about consuming all space */
    block-size: 100%;
  }

  & > header {
    /* defend against 
needing 100% */ flex-shrink: 0; /* fixes cross browser quarks */ min-block-size: fit-content; } }

Mengarahkan kembali ke diagram 3 scroll penuh warna:

  • <header> kini siap menjadi (merah muda) container scroll.
  • <section> siap menjadi scroll (biru) container.

{i>Frame<i} yang saya soroti di bawah ini dengan VisBug membantu kami melihat jendela yang yang telah dibuat oleh container scroll.

elemen {i>header<i} dan bagian memiliki overlay hotpink di atasnya, menguraikan ruang yang mereka ambil dalam komponen

Tata letak tab <header>

Tata letak berikutnya hampir sama: Saya menggunakan {i>flex<i} untuk membuat pengurutan vertikal.

HTML
<snap-tabs>
  <header>
    <nav></nav>
    <span class="snap-indicator"></span>
  </header>
  <section></section>
</snap-tabs>
CSS
header {
  display: flex;
  flex-direction: column;
}

.snap-indicator harus bergerak secara horizontal dengan grup link, dan tata letak {i>header<i} ini membantu mengatur tahap itu. Tidak ada elemen yang diposisikan secara mutlak di sini.

elemen nav dan span.indicator memiliki overlay hotpink di atasnya, yang menguraikan ruang yang ditempatinya dalam komponen

Berikutnya, gaya scroll. Ternyata kita bisa berbagi gaya scroll di antara 2 area gulir horizontal ({i>header<i} dan bagian) sehingga saya membuat utilitas .scroll-snap-x.

.scroll-snap-x {
  /* browser decide if x is ok to scroll and show bars on, y hidden */
  overflow: auto hidden;
  /* prevent scroll chaining on x scroll */
  overscroll-behavior-x: contain;
  /* scrolling should snap children on x */
  scroll-snap-type: x mandatory;

  @media (hover: none) {
    scrollbar-width: none;

    &::-webkit-scrollbar {
      width: 0;
      height: 0;
    }
  }
}

Masing-masing memerlukan overflow pada sumbu x, scroll containment untuk menjebak overscroll, tersembunyi scrollbar untuk perangkat sentuh dan yang terakhir scroll-snap untuk mengunci konten dan presentasi. Urutan tab keyboard kami dapat diakses dan panduan interaksi apa pun fokus secara alami. Penampung snap scroll juga mendapatkan gaya carousel yang bagus dari {i>keyboard<i} mereka.

Tata letak header tab <nav>

Tautan navigasi harus ditata dalam garis, tanpa pemisah baris, secara vertikal di tengah, dan setiap item tautan harus dipaskan ke kontainer scroll-snap. Swift berfungsi untuk CSS 2021!

HTML
<nav>
  <a></a>
  <a></a>
  <a></a>
  <a></a>
</nav>
CSS
  nav {
  display: flex;

  & a {
    scroll-snap-align: start;

    display: inline-flex;
    align-items: center;
    white-space: nowrap;
  }
}

Setiap gaya dan ukuran tautan itu sendiri, sehingga tata letak navigasi hanya perlu menentukan dengan baik. Lebar unik pada item navigasi membuat transisi antar-tab menyenangkan karena indikator menyesuaikan lebarnya dengan target baru. Tergantung berapa banyak elemen yang ada di sini, browser akan me-render scrollbar atau tidak.

elemen navigasi memiliki overlay hotpink di atasnya, menguraikan ruang yang mereka ambil dalam komponen serta di mana mereka meluap

Tata letak tab <section>

Bagian ini adalah item fleksibel dan harus menjadi konsumen ruang yang dominan. Ini juga perlu membuat kolom untuk menempatkan artikel. Sekali lagi, cepat bekerja untuk CSS 2021! block-size: 100% merentangkan elemen ini untuk mengisi induk sebanyak mungkin, maka untuk tata letaknya sendiri, ia membuat serangkaian kolom yang lebarnya 100% dari induk. Persentase berfungsi dengan baik di sini karena kita telah menulis batasan yang kuat pada induknya.

HTML
<section>
  <article></article>
  <article></article>
  <article></article>
  <article></article>
</section>
CSS
  section {
  block-size: 100%;

  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 100%;
}

Ini seperti mengatakan "perluas secara vertikal sebanyak mungkin, dengan cara yang memaksa" (ingatlah header yang kita tetapkan ke flex-shrink: 0: ini adalah perlindungan terhadap push ekspansi), yang menetapkan tinggi baris untuk satu set kolom tinggi penuh. Tujuan Gaya auto-flow memberi tahu petak untuk selalu meletakkan turunan secara horizontal {i>line<i}, tanpa {i>wrapping<i}, persis apa yang kita inginkan; untuk memenuhi jendela induk.

elemen artikel memiliki overlay hotpink di atasnya, yang menguraikan ruang yang ditempati dalam komponen dan di mana mereka meluap

Aku kadang-kadang sulit memahami hal ini! Elemen bagian ini pas ke dalam sebuah kotak, tetapi juga membuat satu set kotak. Saya harap visual dan penjelasan akan sangat membantu.

Tata letak tab <article>

Pengguna harus dapat men-scroll konten artikel, dan scrollbar harus hanya muncul jika ada overflow. Elemen artikel ini ditampilkan dalam posisi Anda. Keduanya merupakan induk scroll dan turunan scroll. Tujuan browser benar-benar menangani beberapa interaksi sentuh, mouse, dan {i>keyboard<i} yang rumit bagi kita di sini.

HTML
<article>
  <h2></h2>
  <p></p>
  <p></p>
  <h2></h2>
  <p></p>
  <p></p>
  ...
</article>
CSS
article {
  scroll-snap-align: start;

  overflow-y: auto;
  overscroll-behavior-y: contain;
}

Aku memilih untuk memasukkan artikel dalam scroller induk mereka. Saya sangat suka cara item tautan navigasi dan elemen artikel terhubung dengan awalan di penampung scrollnya masing-masing. Terlihat dan terasa seperti harmonis hubungan.

elemen artikel dan elemen turunannya memiliki overlay hotpink di atasnya, yang menguraikan ruang yang dibutuhkan dalam komponen dan arahnya meluap

Artikel ini adalah turunan petak, dan ukurannya telah ditentukan sebagai area tampilan area yang ingin kita berikan UX scroll. Ini berarti saya tidak membutuhkan tinggi atau lebar di sini, saya hanya perlu menentukan bagaimana overflow. Saya mengatur {i> overflow-y<i} ke {i>auto<i}, dan juga menjebak interaksi scroll dengan perilaku overscroll yang praktis saat ini.

Rangkuman 3 area scroll

Di bawah ini saya memilih "selalu tampilkan scrollbar" di setelan sistem saya. Sepertinya sangat penting bagi tata letak agar berfungsi dengan mengaktifkan setelan ini, karena adalah langkah untuk meninjau tata letak dan orkestrasi scroll.

3 scrollbar disetel untuk ditampilkan, kini menggunakan ruang tata letak, dan komponen kita masih terlihat bagus

Menurut saya, melihat gutter scrollbar di komponen ini membantu menunjukkan dengan jelas di mana area scroll, arah yang didukung, dan cara area tersebut berinteraksi sama lain. Pertimbangkan bagaimana setiap bingkai jendela {i>scroll<i} ini lentur atau induk petak ke tata letak.

DevTools dapat membantu kita memvisualisasikan ini:

area scroll memiliki overlay alat petak dan flexbox, yang menguraikan ruang yang diperlukan dalam komponen dan arahnya meluap
Chromium Devtools, yang menampilkan tata letak elemen navigasi flexbox yang penuh dengan elemen anchor, tata letak bagian petak yang penuh dengan elemen artikel, serta artikel elemen yang penuh dengan paragraf dan elemen tajuk (heading).

Tata letak scroll sudah lengkap: snapping, deep linkable, dan keyboard dapat diakses. Fondasi yang kuat untuk peningkatan, gaya, dan kesenangan UX.

Sorotan fitur

Scroll yang dipaskan mempertahankan posisi terkuncinya selama pengubahan ukuran. Artinya JavaScript tidak perlu menampilkan apa pun pada rotasi perangkat atau browser ubah ukuran. Coba di Chromium DevTools Perangkat Mode menurut memilih mode apa pun selain Responsive, lalu mengubah ukuran bingkai perangkat. Perhatikan elemen tetap terlihat dan terkunci dengan kontennya. Hal ini telah tersedia sejak Chromium memperbarui implementasinya agar sesuai dengan spesifikasi. Berikut postingan blog tentang hal itu.

Animasi

Tujuan kerja animasi di sini adalah untuk secara jelas menautkan interaksi dengan UI masukan. Hal ini membantu memandu atau membantu pengguna melaluinya (semoga) penemuan semua konten dengan lancar. Saya akan menambahkan {i>motion <i}dengan tujuan dan bersyarat. Pengguna kini dapat menentukan gerakan mereka preferensi dalam sistem operasinya, dan saya senang merespons preferensi mereka dalam antarmuka saya.

Saya akan menautkan tab garis bawah dengan posisi scroll artikel. {i>Snapping<i} bukan hanya kerataan yang bagus, tetapi juga menambatkan awal dan akhir animasi. Ini akan mempertahankan <nav>, yang bertindak seperti peta mini, yang terhubung ke konten. Kita akan memeriksa preferensi gerakan pengguna dari CSS dan JS. Terdapat beberapa tempat bagus untuk dipertimbangkan!

Perilaku scroll

Ada peluang untuk meningkatkan perilaku gerakan :target dan element.scrollIntoView(). Secara default, instan. Browser hanya menyetel posisi scroll. Nah, bagaimana jika kita ingin bertransisi ke posisi {i>scroll<i}, alih-alih berkedip di sana?

@media (prefers-reduced-motion: no-preference) {
  .scroll-snap-x {
    scroll-behavior: smooth;
  }
}

Karena kita memperkenalkan {i>motion <i}di sini, dan {i>motion <i}yang tidak dikontrol pengguna (seperti menggulir), kita hanya menerapkan gaya ini jika pengguna tidak memiliki preferensi sistem operasi mereka untuk mengurangi gerakan. Dengan cara ini, kami hanya memperkenalkan {i>motion <i}untuk orang-orang yang menerimanya.

Indikator tab

Tujuan animasi ini adalah untuk membantu mengaitkan indikator dengan status konten tersebut. Saya memutuskan untuk mewarnai gaya border-bottom crossfade untuk pengguna yang lebih menyukai gerakan yang dikurangi, dan gerakan geser yang ditautkan + animasi pudar warna bagi pengguna yang tidak terbiasa dengan gerakan.

Di Chromium Devtools, saya dapat mengaktifkan/menonaktifkan preferensi dan mendemonstrasikan 2 gaya transisi yang berbeda. Saya sangat senang membuat ini.

@media (prefers-reduced-motion: reduce) {
  snap-tabs > header a {
    border-block-end: var(--indicator-size) solid hsl(var(--accent) / 0%);
    transition: color .7s ease, border-color .5s ease;

    &:is(:target,:active,[active]) {
      color: var(--text-active-color);
      border-block-end-color: hsl(var(--accent));
    }
  }

  snap-tabs .snap-indicator {
    visibility: hidden;
  }
}

Saya menyembunyikan .snap-indicator ketika pengguna lebih memilih pengurangan gerakan karena saya tidak membutuhkannya lagi. Lalu saya menggantinya dengan gaya border-block-end dan transition. Perhatikan juga di interaksi tab bahwa item navigasi aktif tidak hanya memiliki sorotan garis bawah merek, tetapi warna teksnya juga lebih gelap. Tujuan {i>active element<i} memiliki kontras warna teks yang lebih tinggi dan aksen {i>underlight<i} yang cerah.

Hanya beberapa baris CSS tambahan akan membuat seseorang merasa diperhatikan (dalam arti bahwa kami menghormati preferensi gerakan mereka). Saya menyukainya.

@scroll-timeline

Di bagian atas, saya menunjukkan cara menangani pengurangan crossfade gerakan gaya, dan di bagian ini saya akan menunjukkan cara menautkan indikator dan area scroll bersama-sama. Ini adalah beberapa hal eksperimental yang menyenangkan nanti. Saya harap Anda bersemangat seperti saya.

const { matches:motionOK } = window.matchMedia(
  '(prefers-reduced-motion: no-preference)'
);

Pertama-tama, saya memeriksa preferensi gerakan pengguna dari JavaScript. Jika hasil ini adalah false, yang berarti pengguna lebih memilih gerakan yang lebih sedikit, maka kita tidak akan menjalankan dari efek gerakan penautan {i>scroll<i}.

if (motionOK) {
  // motion based animation code
}

Pada saat penulisan ini, dukungan browser untuk @scroll-timeline bukan satu pun. Ini adalah draf spesifikasi hanya dengan implementasi eksperimental. Ini memiliki polyfill, yang saya gunakan dalam demo.

ScrollTimeline

Meskipun CSS dan JavaScript dapat membuat linimasa scroll, saya memilih untuk ikut serta JavaScript, jadi saya bisa menggunakan pengukuran elemen langsung dalam animasi.

const sectionScrollTimeline = new ScrollTimeline({
  scrollSource: tabsection,  // snap-tabs > section
  orientation: 'inline',     // scroll in the direction letters flow
  fill: 'both',              // bi-directional linking
});

Saya ingin 1 hal mengikuti posisi scroll yang lain, dan dengan membuat ScrollTimeline Saya menentukan driver link scroll, scrollSource. Biasanya animasi di web dijalankan dengan jangka waktu global, tetapi dengan sectionScrollTimeline kustom di memori, saya bisa mengubah semua itu.

tabindicator.animate({
    transform: ...,
    width: ...,
  }, {
    duration: 1000,
    fill: 'both',
    timeline: sectionScrollTimeline,
  }
);

Sebelum masuk ke dalam keyframe dalam animasi, saya pikir penting untuk tunjukkan pengikut scroll, tabindicator, akan dianimasikan berdasarkan pada garis waktu khusus, gulir bagian kita. Ini melengkapi penautan, tetapi kehilangan bahan akhir, titik stateful untuk dianimasikan, juga dikenal sebagai keyframe.

Keyframe dinamis

Ada cara CSS deklaratif murni yang sangat ampuh untuk dianimasikan @scroll-timeline, tetapi animasi yang saya pilih terlalu dinamis. Tidak ada beralih di antara lebar auto, dan tidak ada cara untuk membuat secara dinamis jumlah keyframe berdasarkan panjang turunan.

JavaScript tahu cara mendapatkan informasi itu, jadi kita akan mengulanginya pada turunan dan mengambil nilai yang dihitung saat runtime:

tabindicator.animate({
    transform: [...tabnavitems].map(({offsetLeft}) =>
      `translateX(${offsetLeft}px)`),
    width: [...tabnavitems].map(({offsetWidth}) =>
      `${offsetWidth}px`)
  }, {
    duration: 1000,
    fill: 'both',
    timeline: sectionScrollTimeline,
  }
);

Untuk setiap tabnavitem, urai posisi offsetLeft dan tampilkan string yang menggunakannya sebagai nilai translateX. Proses ini akan menghasilkan 4 keyframe transformasi untuk animasi. Hal yang sama dilakukan untuk lebar, masing-masing ditanya berapa lebar dinamisnya dan digunakan sebagai nilai keyframe.

Berikut ini contoh output, berdasarkan preferensi browser dan font saya:

Keyframe TranslateX:

[...tabnavitems].map(({offsetLeft}) =>
  `translateX(${offsetLeft}px)`)

// results in 4 array items, which represent 4 keyframe states
// ["translateX(0px)", "translateX(121px)", "translateX(238px)", "translateX(464px)"]

Frame Utama Lebar:

[...tabnavitems].map(({offsetWidth}) =>
  `${offsetWidth}px`)

// results in 4 array items, which represent 4 keyframe states
// ["121px", "117px", "226px", "67px"]

Untuk meringkas strategi ini, indikator tab kini akan dianimasikan di 4 keyframe bergantung pada posisi pengepasan scroll dari scroller bagian. Penunjuk posisi membuat pemisahan yang jelas antara keyframe dan menambahkan nuansa animasi yang sinkron.

tab aktif dan tab tidak aktif ditampilkan dengan overlay VisBug yang menunjukkan skor kontras yang lewat untuk keduanya

Pengguna menjalankan animasi dengan interaksi mereka, melihat lebar dan posisi indikator berubah dari satu bagian ke bagian berikutnya, melacak dengan menggulir.

Anda mungkin tidak menyadarinya, tetapi saya sangat bangga dengan transisi warna sebagai item navigasi yang disorot menjadi dipilih.

Abu-abu muda yang tidak dipilih akan terlihat semakin terdorong ke belakang saat {i>item<i} memiliki lebih banyak kontras. Sangat umum untuk mengubah warna teks, seperti saat kursor diarahkan dan saat dipilih, tetapi ini adalah tingkat berikutnya untuk mengubah warna saat menggulir, disinkronkan dengan indikator garis bawah.

Begini cara saya melakukannya:

tabnavitems.forEach(navitem => {
  navitem.animate({
      color: [...tabnavitems].map(item =>
        item === navitem
          ? `var(--text-active-color)`
          : `var(--text-color)`)
    }, {
      duration: 1000,
      fill: 'both',
      timeline: sectionScrollTimeline,
    }
  );
});

Setiap link navigasi tab memerlukan animasi warna baru ini, yang melacak scroll yang sama linimasa sebagai indikator garis bawah. Saya menggunakan garis waktu yang sama seperti sebelumnya: sejak perannya adalah memberikan tanda centang pada {i>scroll<i}, kita dapat menggunakan opsi itu dalam animasi yang kita inginkan. Seperti yang saya lakukan sebelumnya, saya membuat 4 keyframe dalam loop, dan kembali warna.

[...tabnavitems].map(item =>
  item === navitem
    ? `var(--text-active-color)`
    : `var(--text-color)`)

// results in 4 array items, which represent 4 keyframe states
// [
  "var(--text-active-color)",
  "var(--text-color)",
  "var(--text-color)",
  "var(--text-color)",
]

Keyframe dengan warna var(--text-active-color) menandai link, dan itu adalah warna teks standar. Terdapat loop bertingkat di sana sehingga mudah, karena loop luar adalah setiap item navigasi, dan loop dalam masing-masing keyframe pribadi navitem. Saya memeriksa apakah elemen loop luar sama dengan {i>loop <i}dalam satu, dan menggunakannya untuk mengetahui kapan dipilih.

Saya sangat senang ketika menulis ini. Banyak sekali.

Bahkan lebih banyak penyempurnaan JavaScript

Perlu diingat bahwa inti dari apa yang saya tunjukkan di sini berfungsi tanpa pada JavaScript. Sekarang, mari kita lihat bagaimana kita dapat meningkatkannya yang tersedia.

Deep link lebih merupakan istilah seluler, tetapi menurut saya tujuan deep link adalah bertemu dengan tab di mana Anda dapat berbagi URL langsung ke konten tab. Tujuan browser akan membuka ID yang cocok dalam hash URL. aku nemu pengendali onload ini membuat efek di seluruh platform.

window.onload = () => {
  if (location.hash) {
    tabsection.scrollLeft = document
      .querySelector(location.hash)
      .offsetLeft;
  }
}

Sinkronisasi akhir scroll

Pengguna kita tidak selalu mengeklik atau menggunakan {i>keyboard<i}, terkadang mereka hanya scroll bebas, sebagaimana mestinya. Saat scroller bagian berhenti untuk men-scroll, di mana pun halamannya mendarat harus dicocokkan di menu navigasi atas.

Inilah cara saya menunggu scroll berakhir: js tabsection.addEventListener('scroll', () => { clearTimeout(tabsection.scrollEndTimer); tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100); });

Setiap kali bagian di-scroll, hapus batas waktu bagian jika ada, dan memulai yang baru. Saat bagian berhenti di-scroll, jangan hapus waktu tunggu, dan api 100 md setelah istirahat. Saat diaktifkan, panggil fungsi yang mencari mengetahui posisi pengguna.

const determineActiveTabSection = () => {
  const i = tabsection.scrollLeft / tabsection.clientWidth;
  const matchingNavItem = tabnavitems[i];

  matchingNavItem && setActiveTab(matchingNavItem);
};

Dengan asumsi scroll telah dipaskan, dengan membagi posisi scroll saat ini dari lebar area scroll harus menghasilkan bilangan bulat dan bukan desimal. Saya kemudian mencoba untuk Ambil navitem dari {i>cache<i} melalui indeks yang dihitung, dan jika menemukan sesuatu, saya mengirimkan kecocokan agar diaktifkan.

const setActiveTab = tabbtn => {
  tabnav
    .querySelector(':scope a[active]')
    .removeAttribute('active');

  tabbtn.setAttribute('active', '');
  tabbtn.scrollIntoView();
};

Pengaturan tab aktif dimulai dengan membersihkan tab apa pun yang sedang aktif, lalu memberi item navigasi yang masuk, atribut {i>active state<i}. Panggilan ke scrollIntoView() memiliki interaksi menyenangkan dengan CSS.

.scroll-snap-x {
  overflow: auto hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;

  @media (prefers-reduced-motion: no-preference) {
    scroll-behavior: smooth;
  }
}

Dalam CSS utilitas snap scroll horizontal, kita telah menyusun bertingkat kueri media yang berlaku smooth men-scroll jika pengguna tahan gerakan. JavaScript dapat dengan bebas membuat untuk menggulir elemen ke dalam tampilan, dan CSS dapat mengelola UX secara deklaratif. Perpaduan kecil yang cukup menyenangkan yang kadang-kadang mereka buat.

Kesimpulan

Sekarang setelah Anda tahu cara saya melakukannya, bagaimana Anda akan melakukannya?! Hal ini membuat suasana menjadi menyenangkan arsitektur komponen! Siapa yang akan membuat versi pertama dengan slot di framework favorit Anda? 🙂

Mari kita diversifikasi pendekatan kami dan mempelajari semua cara untuk membangun di web. Buat Glitch, tweet saya versi Anda, dan akan saya tambahkan ke Remix komunitas di bawah ini.

Remix komunitas