Bagaimana cara menunggu fungsi sibling-count() dan sibling-index()

Fitur-fitur baru tidak hanya muncul di CSS (tapi saya berharap demikian). Sebaliknya, mereka melalui proses diskusi dan pertimbangan yang ekstensif, definisi, penulisan, pembuatan prototipe, pengujian, dukungan, penanganan pengiriman, dan banyak tindakan lainnya yang bahkan tidak dapat saya bayangkan. Proses itu panjangMeskipun saya sangat menginginkan fitur baru, sebagai pengembang sehari-hari, saya tidak bisa menahan diri untuk menunggu.

Namun, saya dapat mengontrol cara saya menunggu: Bisakah saya menghindari semua kemungkinan antarmuka atau demo dengan fitur yang satu ini? Atau apakah saya melampaui batas CSS dan tetap mencoba melakukannya?

Sebagai pengembang yang ambisius dan penuh rasa ingin tahu, banyak dari kita memilih yang terakhir. CSS akan menjadi stagnan tanpa pola pikir ini. Oleh karena itu, hari ini saya ingin melihat dua postingan mendatang: sibling-count() Dan sibling-index(). Kami telah menunggu mereka – selama bertahun-tahun – jadi saya membiarkan keingintahuan alami menguasai diri saya sehingga saya bisa merasakan apa yang membuat saya bergairah. Bergabunglah dengan saya!

-Advertisement-.


Pekerjaan menghitung pohon

Pada titik tertentu, Anda mungkin ingin mengetahui posisi suatu elemen di antara saudara kandungnya atau jumlah anak suatu elemen untuk menghitung sesuatu dalam CSS, mungkin untuk beberapa animasi luar biasa di mana setiap elemen memiliki penundaan lebih lama, atau mungkin untuk mengubah sebuah elemen background-color Sesuai dengan jumlah saudara-saudaranya. Ini adalah kesepakatan yang sudah lama ditunggu-tunggu di daftar keinginan CSS saya. Ambil rilis GitHub CSSWG dari tahun 2017:

Permintaan fitur. Akan menyenangkan jika bisa digunakan counter() berfungsi di dalam calc() pekerjaan. Ini akan membuka kemungkinan baru terkait tata letak.

Namun, penghitung beroperasi menggunakan string, menjadikannya tidak berguna di dalam file calc() Sebuah fungsi yang berhubungan dengan angka. Kita memerlukan sekumpulan fungsi serupa yang kembali Sebagai bilangan bulat Indeks item dan jumlah saudara kandung. Sepertinya tidak banyak yang perlu ditanyakan. Saat ini kita dapat menanyakan suatu elemen berdasarkan posisi pohonnya menggunakan fungsi tersebut :nth-child() Penentu semu (dan variabelnya), apalagi menanyakan suatu elemen berdasarkan jumlah elemen di dalamnya menggunakan :has() Penentu yang salah.

Untungnya, tahun ini CSSWG setuju untuk menerapkannya sibling-count() Dan sibling-index() Pekerjaan! Dan kami sudah memiliki sesuatu yang tertulis di spesifikasi:

itu sibling-count() Notasi fungsional mewakili, sebagai <integer>jumlah total elemen anak di induk elemen yang pengkodeannya digunakan.

itu sibling-index() Notasi fungsional mewakili, sebagai <integer>indeks item yang notasinya digunakan di antara putra ayahnya. Dia mencintai :nth-child(), sibling-index() Ini adalah 1 yang diindeks.

Berapa lama kita harus menunggu untuk menggunakannya? Awal tahun ini, Adam Argyle mengatakan bahwa “salah satu insinyur Chromium menyebutkan ingin melakukan ini, namun kami belum memiliki ilmu untuk mencobanya. Saya akan membagikannya jika kami sudah memilikinya!” berita di tahun 2025, kita mungkin tidak akan melihatnya dikirimkan dalam waktu dekat, jadi mari kita lakukan apa yang bisa kita lakukan sekarang!

Gosokkan dua batang kayu menjadi satu

Cara terdekat yang bisa kita dapatkan untuk fungsi penghitungan pohon dalam hal sintaksis dan penggunaan adalah dengan menggunakan properti khusus. Namun masalah terbesarnya adalah mengisinya dengan indeks dan integer. Cara paling sederhana dan terpanjang adalah dengan melakukan hard-code masing-masing hanya dengan CSS: kita dapat menggunakan a nth-child() Selector untuk memberikan setiap elemen indeks yang sesuai:

li:nth-child(1) {
  --sibling-index: 1;
}

li:nth-child(2) {
  --sibling-index: 2;
}

li:nth-child(3) {
  --sibling-index: 3;
}

/* and so on... */

angka sibling-count() Persamaannya memiliki perbedaan karena kita perlu menggunakan kueri kuantitas :has() spesifik. Kueri kuantitas memiliki sintaks berikut:

.container:has(> :last-child:nth-child(m)) { }

…Di mana m Ini adalah jumlah item yang ingin kami targetkan. Ia bekerja dengan memeriksa apakah elemen terakhir dalam wadah juga ada nth Item yang kami targetkan; Oleh karena itu, ia hanya berisi sejumlah elemen ini. Anda dapat membuat kueri kuantitas khusus Anda sendiri menggunakan alat dari Temani Afif ini. Dalam hal ini, kueri kuantitatif kami akan terlihat seperti ini:

ol:has(> :nth-child(1)) {
  --sibling-count: 1;
}

ol:has(> :last-child:nth-child(2)) {
  --sibling-count: 2;
}

ol:has(> :last-child:nth-child(3)) {
  --sibling-count: 3;
}

/* and so on... */

Contoh ini sengaja menyoroti jumlah item agar singkatnya, namun seiring bertambahnya daftar, daftar tersebut menjadi tidak dapat dikelola. Kami mungkin dapat menggunakan praprosesor seperti Sass untuk menulisnya untuk kami, namun kami ingin fokus pada solusi CSS sederhana di sini. Misalnya, demo berikut dapat mendukung hingga 12 objek, dan Anda sudah dapat melihat betapa jeleknya kode tersebut.

Termasuk cadangan CodePen

Ini adalah 24 aturan untuk mengetahui indeks dan 12 item untuk Anda yang mencatat skor. Sepertinya kita bisa menurunkan angka tersebut menjadi sesuatu yang lebih mudah dikelola, namun jika kita mengkodekan setiap indeks, kita pasti akan menambah jumlah kode yang kita tulis. Hal terbaik yang bisa kita lakukan adalah menulis ulang CSS kita sehingga kita bisa membuat sarangnya --sibling-index Dan --sibling-count properti bersama-sama. Daripada menulis setiap properti satu per satu:

li:nth-child(2) {
  --sibling-index: 2;
}

ol:has(> :last-child:nth-child(2)) {
  --sibling-count: 2;
}

Kita malah bisa tumpang tindih --sibling-count Aturan di dalam --sibling-index sebuah pangkalan.

li:nth-child(2) {
  --sibling-index: 2;

  ol:has(> &:last-child) {
    --sibling-count: 2;
  }
}

Meskipun kelihatannya konyol untuk menyematkan induk ke dalam anaknya, kode CSS berikut ini benar-benar valid; Kami memilih yang kedua li elemen, dan di dalamnya, kita membuat pilihan ol Elemen jika yang kedua li Item tersebut juga merupakan item terakhir, jadi daftarnya hanya berisi dua item. Sintaks mana yang paling mudah dikelola? Terserah kamu.

Termasuk cadangan CodePen

Namun ini hanya sedikit perbaikan. Jika kita mempunyai, misalnya, 100 item, kita masih perlu menyandikannya --sibling-index Dan --sibling-count Properti 100 kali. Untungnya, metode berikut ini akan menambah aturan secara logaritmik, khususnya basis 2. Jadi, daripada menulis 100 aturan untuk 100 elemen, kita akan menulis sekitar 10 aturan untuk sekitar 100 elemen.

Batu api dan baja

Metode ini pertama kali dijelaskan oleh Roman Komarov pada bulan Oktober tahun lalu, di mana ia mempresentasikan prototipe penghitungan pohon dan fungsi masa depan random() pekerjaan. Ini postingan yang bagus, jadi saya sangat menganjurkan Anda untuk membacanya.

Metode ini juga menggunakan properti khusus, namun alih-alih mengkodekan masing-masing properti secara keras, kita akan menggunakan dua properti khusus yang akan membuat file --sibling-index Properti untuk setiap elemen. Agar konsisten dengan postingan Roman, kami akan menghubungi mereka --si1 Dan --si2keduanya dimulai dari 0:

li {
  --si1: 0;
  --si2: 0;
}

Nyata --sibling-index Mereka akan dibangun menggunakan keduanya dan properti faktor (F) mewakili bilangan bulat yang lebih besar dari atau sama dengan 2 Ini memberi tahu kita berapa banyak elemen yang dapat kita identifikasi berdasarkan rumus sqrt(F) - 1. Jadi…

  • Untuk seorang pekerja 2kita bisa memilih 3 Elemen.
  • Untuk seorang pekerja 3kita bisa memilih 8 Elemen.
  • Untuk seorang pekerja 5kita bisa memilih 24 Elemen.
  • Untuk seorang pekerja 10kita bisa memilih 99 Elemen.
  • Untuk seorang pekerja 25kita bisa memilih 624 Elemen.

Seperti yang bisa Anda lihat, menambah faktor sebanyak satu akan memberi kita keuntungan eksponensial dalam hal jumlah elemen yang bisa kita pilih. Tapi bagaimana semua ini diterjemahkan ke dalam CSS?

Hal pertama yang harus diketahui adalah rumus perhitungannya --sibling-index Kepemilikan adalah calc(F * var(--si2) + var(--si1)). Jika kita mengambil sebuah faktor 3akan terlihat seperti ini:

li {
  --si1: 0;
  --si2: 0;

  /* factor of 3; it's a harcoded number */
  --sibling-index: calc(3 * var(--si2) + var(--si1));
}

Parameter berikut mungkin acak tetapi tetap bersama saya di sini. ke --si1 Properti, kita akan menulis aturan untuk memilih elemen yang merupakan kelipatan faktor dan mengembalikannya ke satu 1 Sampai kita tiba F - 1lalu sesuaikan --si1 Untuk perpindahan. Ini diterjemahkan ke CSS berikut:

li:nth-child(Fn + 1) { --si1: 1; }
li:nth-child(Fn + 2) { --si1: 2; }
/* ... */
li:nth-child(Fn+(F-1)) { --si1: (F-1) }

Jadi jika pekerja kita adalah 3kami akan menulis aturan berikut sampai kami tiba F-1Jadi 2 aturan:

li:nth-child(3n + 1) { --si1: 1; }
li:nth-child(3n + 2) { --si1: 2; }

ke --si2 Properti, kami akan menulis aturan untuk memilih item dalam batch pekerja (jadi jika pekerja kami adalah 3kami akan memilih 3 elemen untuk setiap aturan), dimulai dari indeks terakhir yang mungkin (dalam hal ini 8) mundur sehingga kita tidak bisa begitu saja memilih lebih banyak item secara berkelompok. Ini lebih rumit ketika menulis dalam CSS:

li:nth-child(n + F*1):nth-child(-n + F*1-1){--si2: 1;}
li:nth-child(n + F*2):nth-child(-n + F*2-1){--si2: 2;}
/* ... */
li:nth-child(n+(F*(F-1))):nth-child(-n+(F*F-1)) { --si2: (F-1) }

Sekali lagi, jika pekerja kita adalah 3Kami akan menulis dua aturan berikut:

li:nth-child(n + 3):nth-child(-n + 5) {
  --si2: 1;
}
li:nth-child(n + 6):nth-child(-n + 8) {
  --si2: 2;
}

Dan itu saja! Dengan menetapkan hanya dua nilai ini --si1 Dan --si2 Kami dapat mengandalkannya 8 Jumlah item. Perhitungan di balik cara kerjanya tampak aneh pada awalnya, tetapi setelah Anda memahaminya secara visual, itu berhasil. Saya telah membuat demo interaktif di mana Anda dapat melihat bagaimana semua elemen dapat diakses menggunakan rumus ini. Arahkan kursor ke cuplikan kode untuk melihat item mana yang dapat dipilih, lalu klik setiap cuplikan untuk menggabungkannya ke dalam indeks yang memungkinkan.

Termasuk cadangan CodePen

Jika saya memindahkan item dan faktornya ke maksimal, Anda dapat melihat bahwa kami dapat memilih 48 item hanya dengan menggunakan 14 cuplikan!

Tunggu, ada satu hal yang hilang: sibling-count() pekerjaan. Untungnya, kami akan menggunakan kembali semua yang kami pelajari dari prototipe --sibling-index. Kita akan mulai dengan dua properti khusus: --sc1 Dan --sc1 Di dalam wadah, keduanya memulai dari 0 Juga. Rumus perhitungan --sibling-count diri.

ol {
  --sc1: 0;
  --sc2: 0;

  /* factor of 3; also a harcoded number */
  --sibling-count: calc(3 * var(--sc2) + var(--sc1));
}

Postingan Roman juga menjelaskan cara menulis penyeleksi --sibling-count Properti itu sendiri, tapi akan kami gunakan :has() Cara pemilihannya adalah dari cara pertama kita sehingga kita tidak perlu menulis penyeleksi tambahan. Kita bisa menjejalkannya --sc1 Dan --sc2 Properti dalam aturan tempat kita menentukan sibling-index() Properti:

/* --si1 and --sc1 */
li:nth-child(3n + 1) {
  --si1: 1;

  ol:has(> &:last-child) {
    --sc1: 1;
  }
}

li:nth-child(3n + 2) {
  --si1: 2;

  ol:has(> &:last-child) {
    --sc1: 2;
  }
}

/* --si2 and --sc2 */
li:nth-child(n + 3):nth-child(-n + 5) {
  --si2: 1;

  ol:has(> &:last-child) {
    --sc2: 1;
  }
}

li:nth-child(n + 6):nth-child(-n + 8) {
  --si2: 2;

  ol:has(> &:last-child) {
    --sc2: 2;
  }
}

Ini menggunakan operator 3sehingga kita dapat menghitung hingga delapan elemen dengan hanya empat basis. Contoh berikut memiliki faktor 7jadi kita bisa menghitung hingga 48 elemen hanya dengan 14 aturan.

Termasuk cadangan CodePen

Metode ini bagus, tetapi mungkin bukan yang terbaik untuk semua orang karena cara kerjanya yang ajaib, atau hanya karena menurut Anda metode ini tidak estetis. Meskipun menyalakan api dengan batu api dan baja mudah dilakukan oleh tangan yang haus, banyak yang tidak mau menyalakan api.

Menggunakan penyembur api

Dalam metode ini, kita akan kembali menggunakan properti khusus untuk meniru fungsi penghitungan pohon, dan lebih baik lagi, kita akan menulis kurang dari 20 baris kode untuk menghitung hingga tak terbatas – atau menurut saya begitu 1.7976931348623157e+308yang merupakan batas floating point presisi ganda!

Kami akan menggunakan Mutation Observer API, jadi tentu saja memerlukan JavaScript. Saya tahu ini terdengar seperti mengakui kekalahan bagi banyak orang, tapi saya tidak setuju. Jika metode JavaScript lebih sederhana (dan dalam hal ini memang lebih sederhana), itulah cara yang tepat. Sebagai catatan tambahan, jika kinerja adalah perhatian utama Anda, pertahankan pengkodean keras pada setiap indeks dalam CSS atau HTML.

Pertama, kita akan mengambil container kita dari DOM:

const elements = document.querySelector("ol");

Kemudian kita akan membuat fungsi yang mendefinisikan --sibling-index Properti di setiap elemen adalah --sibling-count Di dalam wadah (dapat diakses oleh anak-anaknya karena adanya air terjun). ke --sibling-indexKita harus terbang melewatinya elements.childrenDan kita bisa mendapatkannya --sibling-count dari elements.children.length.

const updateCustomProperties = () => {
  let index = 1;

  for (element of elements.children) {
    element.style.setProperty("--sibling-index", index);
    index++;
  }

  elements.style.setProperty("--sibling-count", elements.children.length);
};

Setelah kita memiliki fungsinya, ingatlah untuk memanggilnya sekali sehingga kita mendapatkan properti awal untuk menghitung pohon:

updateCustomProperties();

Terakhir, monitor lonjakan arus. Kita perlu memulai penggunaan pengamat baru MutationObserver Konstruktor. Ini memerlukan panggilan balik yang dipanggil setiap kali elemen berubah, jadi kami menulis propertinya updateCustomProperties pekerjaan. Dengan hasilnya observer Objeknya, kita bisa menyebutnya observe() Metode ini mengambil dua parameter:

  1. Item yang ingin kami pantau, dan
  2. A config Sebuah objek mendefinisikan apa yang ingin kita amati melalui tiga properti logis: attributes, childListDan subtree. Dalam hal ini, kami hanya ingin memeriksa perubahan pada submenu, jadi kami setel ke true:
const observer = new MutationObserver(updateCustomProperties);
const config = {attributes: false, childList: true, subtree: false};
observer.observe(elements, config);

Hanya itu yang kami butuhkan! Dengan menggunakan metode ini kita dapat menghitung banyak item, pada demo berikut saya atur maksimal 100Tapi itu bisa dengan mudah menjadi sepuluh kali lipat:

Sertakan Cadangan CodePen

Jadi, ya, itulah penyembur api kami di sana. Hal ini tentu saja memicu kebakaran, tetapi hal ini berlebihan untuk sebagian besar kasus penggunaan. Tapi inilah yang kita miliki sementara kita menunggu pemantik api yang sempurna.

Informasi dan tutorial lebih lanjut

  • Kemungkinan masa depan CSS: fungsi penghitungan pohon dan nilai acak (Roman Komarov)
  • Pertunjukan Transformasi yang Menakjubkan (Chris Cowher)
  • Katalog barang (Chris Cowher)
  • Aktifkan penggunaan counter() di dalam calc() #1026
  • Saran: Tambahkan sibling-count() Dan sibling-index() #4559
  • meluas sibling-index() Dan sibling-count() Dengan argumen tertentu #9572
  • sebuah tawaran: children-count() Pekerjaan No.11068
  • sebuah tawaran: descendant-count() Pekerjaan No.11069

Cara menunggu fungsi sibling-count() dan sibling-index() awalnya diterbitkan di CSS-Tricks, yang merupakan bagian dari keluarga DigitalOcean. Anda harus mendapatkan buletin.

Sumber

-Advertisement-.

IDJ