Mengapa browser cocok dengan pemilih CSS dari kanan ke kiri?

560

Selektor CSS dicocokkan oleh mesin peramban dari kanan ke kiri. Jadi mereka pertama-tama menemukan anak-anak dan kemudian memeriksa orang tua mereka untuk melihat apakah mereka cocok dengan bagian peraturan lainnya.

  1. Kenapa ini?
  2. Apakah hanya karena spek itu mengatakan?
  3. Apakah itu memengaruhi tata letak akhirnya jika dievaluasi dari kiri ke kanan?

Bagi saya cara paling sederhana untuk melakukannya adalah menggunakan penyeleksi dengan jumlah elemen paling sedikit. Jadi ID pertama (karena mereka hanya mengembalikan 1 elemen). Kemudian mungkin kelas atau elemen yang memiliki jumlah node paling sedikit - mis. Mungkin hanya ada satu rentang pada halaman jadi langsung ke node itu dengan aturan apa pun yang merujuk pada rentang.

Berikut adalah beberapa tautan yang mendukung klaim saya

  1. http://code.google.com/speed/page-speed/docs/rendering.html
  2. https://developer.mozilla.org/en/Writing_Efficient_CSS

Kedengarannya seperti itu dilakukan dengan cara ini untuk menghindari harus melihat semua anak-anak dari orang tua (yang bisa banyak) daripada semua orang tua dari anak yang harus menjadi satu. Bahkan jika DOM dalam, itu hanya akan melihat satu node per level daripada multiple dalam pencocokan RTL. Apakah lebih mudah / lebih cepat untuk mengevaluasi pemilih CSS LTR atau RTL?

tgandrews
sumber
5
3. Tidak - tidak peduli bagaimana Anda membacanya, pemilih selalu cocok dengan set elemen yang sama.
Šime Vidas
39
Untuk apa nilainya, peramban tidak dapat menganggap bahwa ID Anda unik. Anda dapat menempel id yang sama = "foo" di seluruh DOM Anda, dan #foopemilih harus mencocokkan semua node tersebut. jQuery memiliki opsi untuk mengatakan bahwa $ ("# foo") akan selalu mengembalikan hanya satu elemen, karena mereka mendefinisikan API mereka sendiri dengan aturannya sendiri. Tetapi browser perlu mengimplementasikan CSS, dan CSS mengatakan untuk mencocokkan semua yang ada dalam dokumen dengan ID yang diberikan.
Boris Zbarsky
4
@Quentin Dalam ID dokumen "nonconformant" (to HTML) bisa tidak unik, dan dalam dokumen-dokumen itu CSS membutuhkan pencocokan semua elemen dengan ID itu. CSS sendiri tidak menempatkan persyaratan normatif pada ID yang unik; teks yang Anda kutip informatif.
Boris Zbarsky
5
@Boris Zbarsky Apa yang dilakukan jQuery tergantung pada jalur kode dalam jQuery. Dalam beberapa kasus, jQuery menggunakan NodeSelector API (asli querySelectorAll). Dalam kasus lain, Sizzle digunakan. Sizzle tidak cocok dengan beberapa ID tetapi QSA melakukannya (AYK). Jalur yang diambil tergantung pada pemilih, konteks, dan browser serta versinya. API Query jQuery menggunakan apa yang saya sebut "Native First, Dual Approach". Saya menulis artikel tentang itu, tetapi itu turun. Meskipun Anda dapat menemukan di sini: fortybelow.ca/hosted/dhtmlkitchen/JavaScript-Query-Engines.html
Garrett
5
Aku bahkan tidak yakin siapa pun kecuali kalian berdua bisa mengetahui apa yang terjadi dengan percakapan selama ini. Kami memang punya obrolan untuk diskusi panjang ini. Apa pun yang benar-benar ingin Anda pertahankan harus dimasukkan ke dalam pertanyaan atau jawaban, terutama jika itu menjelaskan informasi. Stack Overflow tidak menangani diskusi dalam komentar dengan baik.
George Stocker

Jawaban:

824

Ingatlah bahwa ketika browser melakukan pencocokan pemilih, ia memiliki satu elemen (elemen yang mencoba menentukan gaya) dan semua aturan Anda dan pemilihnya dan perlu menemukan aturan mana yang cocok dengan elemen tersebut. Ini berbeda dari hal jQuery yang biasa, katakanlah, di mana Anda hanya memiliki satu pemilih dan Anda perlu menemukan semua elemen yang cocok dengan pemilih itu.

Jika Anda hanya memiliki satu pemilih dan hanya satu elemen untuk dibandingkan dengan pemilih itu, maka dari kiri ke kanan lebih masuk akal dalam beberapa kasus. Tapi itu jelas bukan situasi browser. Peramban mencoba merender Gmail atau apa pun dan memiliki peramban yang sedang <span>ia coba gayakan dan 10.000+ aturan yang dimasukkan Gmail ke dalam stylesheet-nya (saya tidak mengarang angka itu).

Secara khusus, dalam situasi browser sedang melihat sebagian besar pemilih yang dianggapnya tidak cocok dengan elemen yang dimaksud. Jadi masalahnya menjadi salah satu memutuskan bahwa pemilih tidak cocok secepat mungkin; jika itu membutuhkan sedikit kerja ekstra dalam kasus yang cocok Anda masih menang karena semua pekerjaan yang Anda simpan dalam kasus yang tidak cocok.

Jika Anda mulai dengan hanya mencocokkan bagian paling kanan pemilih dengan elemen Anda, maka kemungkinan itu tidak akan cocok dan Anda selesai. Jika cocok, Anda harus melakukan lebih banyak pekerjaan, tetapi hanya sebanding dengan kedalaman pohon Anda, yang tidak terlalu besar dalam kebanyakan kasus.

Di sisi lain, jika Anda mulai dengan mencocokkan bagian paling kiri dari pemilih ... apa yang cocok dengan Anda? Anda harus mulai berjalan DOM, mencari node yang mungkin cocok dengannya. Hanya menemukan bahwa tidak ada yang cocok dengan bagian paling kiri mungkin perlu waktu.

Jadi browser cocok dari kanan; itu memberikan titik awal yang jelas dan memungkinkan Anda menyingkirkan sebagian besar pemilih pemilih dengan sangat cepat. Anda dapat melihat beberapa data di http://groups.google.com/group/mozilla.dev.tech.layout/browse_thread/thread/b185e455a0b3562a/7db34de545c17665 (meskipun notasinya membingungkan), tetapi hasilnya adalah untuk Gmail khususnya dua tahun lalu, untuk 70% dari pasangan (aturan, elemen) Anda dapat memutuskan bahwa aturan tersebut tidak cocok setelah hanya memeriksa bagian tag / class / id dari pemilih paling kanan untuk aturan tersebut. Angka yang sesuai untuk suite tes kinerja pageload Mozilla adalah 72%. Jadi, sangat layak mencoba menyingkirkan 2/3 dari semua aturan secepat yang Anda bisa dan kemudian hanya khawatir tentang mencocokkan 1/3 yang tersisa.

Perhatikan juga bahwa ada optimasi browser lain yang sudah dilakukan untuk menghindari bahkan mencoba untuk mencocokkan aturan yang pasti tidak cocok. Misalnya, jika pemilih paling kanan memiliki id dan id itu tidak cocok dengan id elemen, maka tidak akan ada upaya untuk mencocokkan pemilih itu dengan elemen itu sama sekali di Gecko: set "penyeleksi dengan ID" yang dicoba berasal dari pencarian hashtable pada ID elemen. Jadi ini adalah 70% dari aturan yang memiliki peluang pencocokan yang cukup bagus yang masih belum cocok setelah hanya mempertimbangkan tag / kelas / id dari pemilih paling kanan.

Boris Zbarsky
sumber
5
Sebagai sedikit bonus, akan lebih masuk akal untuk membacanya RTL daripada LTR bahkan dalam bahasa Inggris. Contoh: stackoverflow.com/questions/3851635/css-combinator-precedence/…
BoltClock
6
Perhatikan bahwa pencocokan RTL hanya berlaku di kombinator . Itu tidak menelusuri ke tingkat pemilih sederhana. Yaitu, browser mengambil pemilih senyawa paling kanan , atau urutan pemilih sederhana dan upaya untuk mencocokkannya secara atom. Kemudian, jika ada kecocokan, itu mengikuti kombinator ke kiri ke pemilih senyawa berikutnya dan memeriksa elemen di posisi itu, dan seterusnya. Tidak ada bukti bahwa peramban membaca setiap bagian RTL pemilih majemuk; pada kenyataannya, paragraf terakhir menunjukkan sebaliknya (pemeriksaan id selalu didahulukan).
BoltClock
5
Sebenarnya, pada saat Anda mencocokkan penyeleksi, setidaknya di Gecko, tagname dan namespace didahulukan. ID (serta tagname dan nama kelas) dipertimbangkan dalam langkah pra-pemfilteran yang menghilangkan sebagian besar aturan tanpa benar-benar berusaha mencocokkan pemilih.
Boris Zbarsky
Itu mungkin membantu tergantung pada optimasi apa yang dilakukan UA, tetapi itu tidak akan membantu langkah pra-penyaringan di Gecko yang saya jelaskan di atas. Ada langkah pemfilteran kedua yang berfungsi pada ID dan kelas yang hanya digunakan untuk kombinator keturunan yang mungkin bisa membantu.
Boris Zbarsky
@ Benito Ciaro: Belum lagi masalah kekhususan juga.
BoltClock
33

Parsing kanan ke kiri, juga disebut parsing bottom-up sebenarnya efisien untuk browser.

Pertimbangkan yang berikut ini:

#menu ul li a { color: #00f; }

Peramban pertama-tama memeriksa a, lalu li, lalu ul, dan kemudian #menu.

Ini karena saat browser memindai halaman, ia hanya perlu melihat elemen / node saat ini dan semua node / elemen sebelumnya yang telah dipindai.

Hal yang perlu diperhatikan adalah bahwa browser mulai memproses saat mendapat tag / node lengkap dan tidak perlu menunggu seluruh halaman kecuali ketika ia menemukan skrip, dalam hal ini untuk sementara waktu berhenti dan menyelesaikan eksekusi skrip kemudian maju.

Jika sebaliknya, itu akan menjadi tidak efisien karena browser menemukan elemen itu pemindaian pada pemeriksaan pertama, tetapi kemudian dipaksa untuk terus mencari melalui dokumen untuk semua penyeleksi tambahan. Untuk ini browser harus memiliki seluruh html dan mungkin perlu memindai seluruh halaman sebelum mulai melukis css.

Ini bertentangan dengan bagaimana kebanyakan libs menguraikan dom. Di sana dom dibuat dan tidak perlu memindai seluruh halaman hanya menemukan elemen pertama dan kemudian mencocokkan orang lain di dalamnya.

aWebDeveloper
sumber
20

Hal ini memungkinkan untuk cascading dari yang lebih spesifik ke yang kurang spesifik. Ini juga memungkinkan hubungan pendek dalam aplikasi. Jika aturan yang lebih spesifik berlaku di semua aspek yang diterapkan oleh aturan induk, semua aturan induk diabaikan. Jika ada bit lain di induk, mereka diterapkan.

Jika Anda sebaliknya, Anda akan memformatnya sesuai dengan orang tua dan kemudian menimpa setiap kali anak memiliki sesuatu yang berbeda. Dalam jangka panjang, ini lebih banyak pekerjaan daripada mengabaikan item dalam aturan yang sudah diurus.

Gregory A Beamer
sumber
11
Itu masalah terpisah. Anda melakukan cascading dengan menyortir aturan berdasarkan spesifisitas dan kemudian mencocokkannya dengan urutan spesifisitas. Tapi pertanyaannya di sini adalah mengapa untuk aturan tertentu Anda mencocokkan pemilihnya dengan cara tertentu.
Boris Zbarsky