Untuk mengukur teks DOM dengan andal, tunggu helaian gaya yang berkaitan, letakkan teks dalam susun atur yang dimaksudkan, kemudian tunggu document.fonts.ready sebelum membaca geometri. Panduan ini menunjukkan urutannya dalam pelayar desktop Mac dan menerangkan bila urutan itu perlu diulangi.

Tugas di sini ialah mengukur label sintetik atau perenggan berbalut dalam halaman pelayar—contohnya, menentukan ruang yang diperlukan oleh kad. Pelayar harus mengendalikan susun atur teks; kod anda harus memastikan kesediaan dan membaca kotak yang terhasil.

Isyarat kesediaan yang manakah harus ditunggu?

Terdapat pencapaian berasingan untuk pembinaan dokumen, pemuatan helaian gaya dan susun atur fon. Pilih pencapaian yang sepadan dengan kerja yang anda kawal, kemudian ukur selepas kandungan dan gayanya tersedia.

Isyarat Perkara yang dipastikan Perkara yang masih perlu diberi perhatian
DOMContentLoaded Penghuraian dan pelaksanaan skrip tertunda telah selesai Isyarat ini tidak menunggu helaian gaya secara langsung
Peristiwa load bagi helaian gaya Helaian gaya itu dan kandungan yang diimport telah dimuatkan dan dihuraikan Pemuatan fon dan susun atur sasaran yang terhasil
Peristiwa load bagi tetingkap Sumber halaman awal, termasuk helaian gaya, telah dimuatkan, kecuali sumber malas Perubahan aplikasi kemudian dan kesediaan fon
document.fonts.ready Pemuatan dan susun atur yang melibatkan fon dokumen yang digunakan telah selesai Perubahan kandungan, gaya atau saiz yang berlaku kemudian

MDN menerangkan perkara khusus yang berkaitan dengan helaian gaya: DOMContentLoaded sendiri tidak menunggu helaian gaya, walaupun skrip tertunda menunggunya lalu boleh melambatkan peristiwa itu secara tidak langsung. Isyarat ini berguna untuk mencari elemen, tetapi tidak mencukupi sebagai syarat pengukuran. Document: DOMContentLoaded event.

Bagi helaian gaya yang anda sisipkan sendiri, pasangkan pengendali load dan error sebelum menyisipkan pautan itu. MDN meletakkan peristiwa load helaian gaya selepas pemuatan dan penghuraian, sejurus sebelum gaya mula digunakan. Pemprosesan helaian gaya WHATWG juga membezakan pemuatan yang berjaya daripada peristiwa error. Stylesheet load events, HTML Standard: Stylesheet links.

Bagi pemuatan sumber awal halaman, peristiwa load tetingkap ialah titik semakan yang lebih menyeluruh. Skrip yang bermula selepas itu mesti memeriksa document.readyState sebelum melanggan, kerana peristiwa yang sudah berlaku tidak akan mencetuskan pengendali yang baru dipasang. Kod aplikasi yang menyisipkan helaian gaya kemudian masih perlu menjejak penyisipan itu secara eksplisit. Window: load event, Checking document readiness.

Apakah yang sebenarnya dijamin oleh kesediaan fon?

Spesifikasi W3C CSS Font Loading menerangkan bahawa ready dipenuhi selepas operasi susun atur yang berkaitan selesai dan tiada lagi pemuatan fon diperlukan untuk kerja itu. Ini termasuk keadaan apabila fon sandaran menyebabkan pemuatan tambahan. Janji yang sama hanya dipenuhi sekali, dan perubahan kemudian boleh menyebabkan pemuatan fon selanjutnya. Baca document.fonts.ready selepas perubahan yang berkaitan dengan setiap pengukuran, bukannya menggunakan satu janji permulaan untuk selama-lamanya. CSS Font Loading Module Level 3: The ready attribute.

Kesediaan merujuk kepada fon yang digunakan oleh dokumen. Ini tidak bermakna setiap fon yang diisytiharkan telah dimuat turun. MDN secara khusus menyatakan bahawa font-display: optional boleh menyebabkan set fon yang digunakan berbeza daripada set yang diisytiharkan. Oleh itu, pengukuran yang selesai mungkin menggambarkan teks sandaran dengan tepat. Document: fonts property.

Tentukan hasil yang anda perlukan sebelum menulis kod menunggu:

  • Ukur hasil yang sedang dirender: tunggu kesediaan fon dan baca geometri DOM, dengan menerima pilihan fon sandaran pelayar.
  • Wajibkan muka fon yang boleh dimuat turun dan telah diisytiharkan: muatkan muka fon yang sepadan secara eksplisit, kendalikan kegagalan, kemudian tunggu kesediaan fon dokumen sebelum mengukur.
  • Ukur kandungan yang berubah: ulangi urutan itu selepas setiap kemas kini yang berkaitan dan buang hasil yang digantikan oleh kemas kini lain.

Untuk hasil kedua, document.fonts.load() menerima spesifikasi fon CSS dan teks sampel pilihan. Dengan keluarga sintetik yang diisytiharkan bernama SampleFace, spesifikasinya boleh berupa 400 18px \"SampleFace\", dan teksnya boleh berupa Panel Alpha. Janji yang dipulangkan akan ditolak jika muka fon yang sepadan gagal dimuatkan. Argumen teks memilih muka fon melalui julat Unicode yang diisytiharkan; argumen ini tidak mengesahkan sokongan glyph secara individu. FontFaceSet: load() method.

Jangan gantikan keperluan itu dengan document.fonts.check(). MDN menerangkan bahawa kaedah itu mungkin memulangkan true walaupun fon yang diminta tidak tersedia. Begitu juga, W3C menetapkan bahawa ready tidak pernah ditolak, jadi kejayaan penyelesaian sahaja tidak membuktikan bahawa pemuatan fon berjaya. FontFaceSet: check() method, CSS Font Loading: The ready attribute.

Jalankan contoh lengkap daripada pemuatan helaian gaya hingga pengukuran

Pada Mac anda, buka about:blank dalam pelayar desktop semasa, buka alat pembangunannya dan pilih Console. Jalankan program JavaScript lengkap berikut sekali. Program ini menghasilkan teks sampel neutral dan helaian gayanya sendiri, jadi tidak memerlukan pelayan, pustaka atau fail fon.

Contoh ini sengaja menggunakan system-ui. Contoh ini menunjukkan urutan menunggu tanpa bergantung pada aset yang boleh dimuat turun. Pada halaman yang menggunakan fon web, kekalkan urutan itu dan biarkan pengisytiharan fon sebenar halaman tersebut membekalkan sumbernya. Contoh pelayar ini telah diperiksa tetapi tidak dijalankan untuk manuskrip ini.

(async () => {
  if (!document.fonts || !document.body) {
    throw new Error("A document body and CSS Font Loading support are required.");
  }
  if (document.getElementById("text-measure-demo")) {
    throw new Error("Reload the blank tab before running this example again.");
  }

  async function withDeadline(promise, label, milliseconds = 10000) {
    let timer;
    try {
      return await Promise.race([
        promise,
        new Promise((_, reject) => {
          timer = setTimeout(() => {
            reject(new Error(`${label} did not finish within the deadline.`));
          }, milliseconds);
        })
      ]);
    } finally {
      clearTimeout(timer);
    }
  }

  const paragraph = document.createElement("p");
  paragraph.id = "text-measure-demo";
  paragraph.textContent =
    "Panel Alpha contains a compact label. Panel Beta contains another label.";
  document.body.append(paragraph);

  const css = `
    #text-measure-demo {
      box-sizing: border-box;
      width: 280px;
      margin: 24px;
      padding: 12px;
      border: 1px solid #777;
      font: 400 18px/1.5 system-ui, sans-serif;
      letter-spacing: normal;
      white-space: normal;
      overflow-wrap: normal;
    }
  `;
  const stylesheetURL = URL.createObjectURL(
    new Blob([css], { type: "text/css" })
  );
  const link = document.createElement("link");
  link.rel = "stylesheet";
  link.href = stylesheetURL;

  const stylesheetLoaded = new Promise((resolve, reject) => {
    link.onload = resolve;
    link.onerror = () => reject(new Error("The sample stylesheet failed."));
  });

  try {
    document.head.append(link);
    await withDeadline(stylesheetLoaded, "Stylesheet loading");

    // Establish the target's styled layout before reading font readiness.
    paragraph.getBoundingClientRect();
    await withDeadline(document.fonts.ready, "Font readiness");

    const style = getComputedStyle(paragraph);
    const range = document.createRange();
    range.selectNodeContents(paragraph);
    const copyRect = rect => ({
      x: rect.x, y: rect.y, width: rect.width, height: rect.height
    });

    console.log({
      text: paragraph.textContent,
      fontFamily: style.fontFamily,
      fontSize: style.fontSize,
      lineHeight: style.lineHeight,
      elementBox: copyRect(paragraph.getBoundingClientRect()),
      textFragments: Array.from(range.getClientRects(), copyRect)
    });
  } catch (error) {
    link.remove();
    paragraph.remove();
    throw error;
  } finally {
    link.onload = null;
    link.onerror = null;
    URL.revokeObjectURL(stylesheetURL);
  }
})().catch(error => console.error(error));

Urutan pentingnya ialah penyelesaian helaian gaya, susun atur sasaran yang telah dipastikan, kesediaan fon, kemudian bacaan geometri akhir. Bacaan geometri pertama memastikan susun atur semasa; nilainya sengaja dibuang. Bacaan akhir menggambarkan hasil selepas fon sedia. Had sepuluh saat ialah had operasi sebagai ilustrasi: jika tempoh itu tamat, percubaan yang belum selesai dilaporkan dan tiada pengukuran dihasilkan.

Periksa fontSize dan lineHeight bersama kotak semasa menyesuaikan contoh ini. getComputedStyle() memulangkan nilai sifat yang telah diselesaikan selepas helaian gaya aktif digunakan, jadi ia berguna untuk mengesan ketidakpadanan pemilih atau lata. Nilai fontFamily menerangkan senarai keluarga CSS; nilai itu tidak boleh dianggap sebagai bukti muka fon yang membekalkan setiap glyph. Window: getComputedStyle() method.

Pengukuran DOM yang manakah menjawab soalan anda?

Gunakan element.getBoundingClientRect() apabila anda memerlukan ruang luar yang dipenuhi oleh kad atau perenggan. Segi empat tepatnya merangkumi ruang dalam dan sempadan, dan kedudukannya relatif terhadap port pandang. Dalam sampel ini, lebar kotak sempadan yang diisytiharkan ialah 280px; lebar kandungan yang tersedia untuk teks ialah 254px, dikira sebagai 280 − 24 − 2. Pengiraan itu menerangkan CSS yang dibekalkan, bukannya hasil pelayar yang diperhatikan. Element: getBoundingClientRect() method.

Gunakan Range pada teks apabila anda memerlukan fragmen susun aturnya. Range.getClientRects() memulangkan segi empat tepat bagi julat itu, bukannya sekadar kotak perenggan yang melingkunginya. Kekalkan koleksi itu: meratakan teks berbalut menjadi satu lebar akan menghilangkan maklumat berguna tentang kawasan yang dipenuhinya. Jangan namakan panjang tatasusunan itu lineCount dalam pelaksanaan umum; kandungan sebaris yang kompleks boleh menghasilkan beberapa segi empat tepat. Range: getClientRects() method.

Kekalkan jujukan teks asal semasa mengukurnya. Menambah pembalut pada setiap aksara boleh mewujudkan sempadan pemformatan yang menjejaskan pembentukan aksara. Draf CSS Text W3C menetapkan keadaan apabila sempadan sebaris mengekalkan atau memutuskan pembentukan aksara. Mengukur DOM sedia ada dengan julat mengelakkan pembalut tambahan itu. CSS Text Module Level 3: Shaping Across Element Boundaries.

Canvas mempunyai kegunaan yang berbeza. measureText() memulangkan metrik teks canvas untuk rentetan yang diberikan. Untuk tugas ini—mencari pembalutan sebenar teks dalam kad DOM—baca DOM selepas kesediaan dicapai. Anggap pengukuran canvas sebagai pengiraan berasingan dengan tetapan teksnya sendiri. CanvasRenderingContext2D: measureText() method.

Apakah yang perlu dilakukan apabila pengukuran masih berubah?

Gunakan urutan diagnosis ini sebelum menambah kelewatan lain:

  1. Pastikan penyiapan helaian gaya yang ditunggu adalah milik helaian gaya yang menggayakan sasaran. Bagi helaian yang disisipkan secara dinamik, simpan janji pemuatannya bersama kod penyisipan.
  2. Periksa tipografi dan lebar sasaran yang telah diselesaikan. Helaian gaya yang dimuatkan masih boleh kalah dalam lata.
  3. Pastikan teks sasaran dan bekas yang dimaksudkan tersedia sebelum menunggu kesediaan fon.
  4. Tentukan sama ada fon sandaran boleh diterima. Jika muka fon tertentu diperlukan, gunakan pemuatan eksplisit dan laporkan kegagalan.
  5. Periksa sama ada kemas kini aplikasi lain mengubah teks, kelas atau bekas selepas anda menunggu. Mulakan semula pengukuran untuk keadaan baharu itu.

Panggilan balik bingkai bukan penghalang pemuatan sumber. requestAnimationFrame() menjadualkan panggilan balik sebelum pengecatan semula, dan pelayar lazimnya menjedakannya dalam tab latar belakang. Gunakannya apabila penjadualan bingkai sesuai untuk aplikasi anda, tetapi jangan gantikan janji helaian gaya atau fon dengan satu atau dua panggilan balik bingkai. Window: requestAnimationFrame() method.

Bagi susun atur yang boleh diubah saiznya, perhatikan bekas yang berkaitan dengan ResizeObserver dan jadualkan pengukuran baharu apabila dimensinya berubah. Pemerhati ini melaporkan perubahan saiz kotak kandungan atau kotak sempadan; ia bukan pemberitahuan sejagat tentang perubahan teks. Cetuskan juga pengukuran daripada operasi aplikasi yang menggantikan teks atau mengubah tipografi. ResizeObserver.

Jika anda sudah menggunakan Playwright, jalankan penantian kesediaan dan bacaan geometri di sisi pelayar dalam panggilan balik page.evaluate() tak segerak. Playwright menunggu janji yang dipulangkan dan membawa hasilnya kembali ke persekitaran pemanggil. Pulangkan medan segi empat tepat berangka yang ringkas, seperti yang dilakukan contoh ini, dan hantarkan sebarang nilai dari sisi pemanggil secara eksplisit ke dalam panggilan balik. Evaluating JavaScript.

Pengukuran yang berguna terikat pada teks, keadaan helaian gaya, hasil fon dan saiz bekas yang tertentu. Tetapkan input tersebut, tunggu kerja pemuatan yang berkaitan, kemudian ukur DOM yang utuh. Ulangi apabila input itu berubah supaya geometri yang direkodkan terus menjawab persoalan susun atur yang anda hadapi.

Sumber