ปัญหา
การเปลี่ยนมุมมองข้ามเอกสารจะทำให้องค์ประกอบเคลื่อนไหวระหว่าง 2 หน้าในระหว่างการไปยังส่วนต่างๆ ของต้นทางเดียวกัน เบราว์เซอร์จะจับภาพสแนปชอตของหน้าเก่า นำทาง แล้วแสดงภาพเคลื่อนไหวจากสแนปชอตไปยังหน้าใหม่ หากหน้าใหม่ยังโหลดทรัพยากรที่สำคัญไม่เสร็จ เช่น สไตล์ชีต สคริปต์เลย์เอาต์ หรือองค์ประกอบ DOM ที่สำคัญ การเปลี่ยนภาพจะแสดงภาพเคลื่อนไหวในสถานะที่ไม่สมบูรณ์หรือไม่มีสไตล์ ซึ่งทำให้เกิดข้อบกพร่องด้านภาพ เช่น องค์ประกอบเปลี่ยนไปอยู่ในตำแหน่งที่ไม่ถูกต้อง เนื้อหาเปลี่ยนรูปแบบระหว่างภาพเคลื่อนไหว หรือแบบอักษรสำรองกะพริบเป็นแบบอักษรบนเว็บหลังจากเปลี่ยนผ่านเสร็จสมบูรณ์
วิธีแก้ปัญหา
ใช้ blocking="render" กับองค์ประกอบ <link> และ <script> ที่สำคัญใน <head> ของหน้าใหม่ และใช้ <link rel="expect"> เพื่อบล็อกการแสดงผลจนกว่าจะมีการแยกวิเคราะห์องค์ประกอบ DOM ที่เฉพาะเจาะจง วิธีนี้ช่วยให้มั่นใจว่าเบราว์เซอร์จะไม่เริ่มภาพเคลื่อนไหวของการเปลี่ยนมุมมองจนกว่าสถานะภาพของหน้าใหม่จะเสถียร เบราว์เซอร์จะแยกวิเคราะห์ HTML ต่อไปในเบื้องหลัง โดยจะเลื่อนเฉพาะการแสดงผลเท่านั้น
กลยุทธ์การติดตั้งใช้งาน
- บังคับ: เลือกใช้การเปลี่ยนมุมมองข้ามเอกสารด้วย At-Rule
@view-transitionCSS ในทั้ง 2 หน้า - จำเป็น: ตรวจสอบว่าสไตล์ชีตที่สำคัญอยู่ใน
<head>สไตล์ชีตใน<head>จะบล็อกการแสดงผลโดยค่าเริ่มต้น สไตล์ชีตที่แทรกแบบไดนามิกต้องมีblocking="render"ที่ชัดเจน - ควรใช้
blocking="render"กับองค์ประกอบ<script>ที่ต้องดำเนินการก่อนที่การเปลี่ยนฉากจะเคลื่อนไหว (เช่น สคริปต์ที่ใช้ธีมหรือส่งผลต่อเลย์เอาต์) - ควรใช้
<link rel="expect" href="#element-id" blocking="render">เพื่อบล็อกการแสดงผลจนกว่าจะมีการแยกวิเคราะห์เนื้อหาครึ่งหน้าบน ซึ่งรวมถึงการเปลี่ยนทุกประเภท ได้แก่ การเฟดข้ามแบบเต็มหน้า (เพื่อหลีกเลี่ยงการเคลื่อนไหวไปยังหน้าว่าง) ภาพเคลื่อนไหวแบบมอร์ฟ (เพื่อให้แน่ใจว่ามีองค์ประกอบที่มีชื่ออยู่ใน DOM) และเลย์เอาต์ที่ขึ้นอยู่กับสคริปต์ (เพื่อให้แน่ใจว่าเนื้อหาที่มีสไตล์ได้รับการแยกวิเคราะห์) - อย่าบล็อกการแสดงผลในเนื้อหาที่ไม่สำคัญ บล็อกเฉพาะทรัพยากรและองค์ประกอบที่ส่งผลต่อวิวพอร์ตเริ่มต้นเท่านั้น การบล็อกเนื้อหามากเกินไปจะทำให้การเปลี่ยนผ่านล่าช้าและลดประสิทธิภาพที่รับรู้ได้
คู่มือการติดตั้งใช้งาน
ขั้นตอนที่ 1: เลือกใช้การเปลี่ยนมุมมองข้ามเอกสาร
บังคับ: ทั้งหน้าต้นทางและปลายทางต้องมี At-rule @view-transition หากไม่มีสิ่งนี้ จะไม่มีการเปลี่ยนข้ามเอกสารเกิดขึ้น
/*
MANDATORY: Include this rule in every page that participates
in cross-document view transitions.
`navigation: auto` enables transitions for standard navigations
(link clicks, form submissions, back/forward).
*/
@view-transition {
navigation: auto;
}
/* MANDATORY Copy-Paste Safety: Disable cross-document view transitions for users requesting reduced motion */
@media (prefers-reduced-motion: reduce) {
@view-transition {
navigation: none;
}
}
ขั้นตอนที่ 2: บล็อกการแสดงผลจนกว่าสคริปต์ที่สำคัญจะทำงาน
หากสคริปต์ที่ไม่บล็อกใน <head> ต้องทำงานก่อนที่การเปลี่ยนภาพจะเคลื่อนไหว (เช่น เพื่อใช้คลาสธีมหรือส่งผลต่อเลย์เอาต์) ให้ทำเครื่องหมายด้วย blocking="render" หากไม่มีสคริปต์ async, defer หรือ type="module" อาจทำงานหลังจากที่การเปลี่ยนผ่านเริ่มต้นขึ้นแล้ว
<head>
<!--
DO: Mark layout-critical scripts with blocking="render".
-->
<script type=module blocking="render">
// Example: apply a stored theme before the page renders,
// so the transition snapshot reflects the correct theme.
document.documentElement.dataset.theme =
localStorage.getItem('theme') || 'light';
</script>
</head>
ขั้นตอนที่ 3: บล็อกการแสดงผลจนกว่าจะแยกวิเคราะห์องค์ประกอบ DOM หลัก
สไตล์ชีตและสคริปต์ blocking="render" ใน <head> รับประกันได้เพียงว่า <head> ได้รับการประมวลผลอย่างสมบูรณ์แล้ว โดยจะไม่รอให้แยกวิเคราะห์เนื้อหา <body> หากไม่มีการบล็อกเพิ่มเติม เบราว์เซอร์อาจจับภาพหน้าใหม่ก่อนที่องค์ประกอบครึ่งหน้าบนจะอยู่ใน DOM ซึ่งส่งผลให้เกิดการเปลี่ยนภาพที่เคลื่อนไหวไปยังหน้าว่างหรือหน้าเว็บที่แสดงผลบางส่วน
<link rel="expect"> แก้ปัญหานี้โดยการบล็อกการแสดงผลจนกว่าจะมีการแยกวิเคราะห์องค์ประกอบที่เฉพาะเจาะจง (ระบุโดย id) ค่า href ต้องเป็นตัวระบุส่วนย่อย (เช่น #hero) ที่ตรงกับแอตทริบิวต์ id ขององค์ประกอบเป้าหมาย เมื่อแยกวิเคราะห์แท็กปิดขององค์ประกอบนั้นแล้ว ระบบจะปล่อยบล็อกการแสดงผล โปรดทราบว่า <link rel="expect"> รับประกันเฉพาะว่าองค์ประกอบ DOM เป้าหมายมีอยู่ในเอกสารเท่านั้น แต่ไม่ได้รอให้ทรัพยากรย่อยภายนอก (เช่น พิกเซล <img>) ดาวน์โหลดหรือถอดรหัส
ควรใช้ <link rel="expect"> ในสถานการณ์ต่อไปนี้ทั้งหมด
กรณีการใช้งานที่ 1: การเฟดข้ามแบบเต็มหน้า
แม้ว่าองค์ประกอบแต่ละรายการจะไม่มี view-transition-name แต่การเปลี่ยน root เริ่มต้นจะทำให้ทั้งหน้าจางเข้าหากัน หากระบบจับภาพหน้าจอของหน้าใหม่ก่อนที่จะแยกวิเคราะห์เนื้อหาครึ่งหน้าบน ภาพครอสเฟดจะเคลื่อนไหวจากหน้าเก่าไปยังหน้าว่างหรือหน้าเว็บที่ไม่สมบูรณ์ บล็อกการแสดงผลในองค์ประกอบที่ทำเครื่องหมายจุดสิ้นสุดของเนื้อหาที่มองเห็นได้ในครึ่งหน้าบน
<head>
<link rel="stylesheet" href="/css/styles.css">
<!--
DO: Block rendering until the main content area is parsed,
even for a simple cross-fade. Without this, the browser may
snapshot the page before visible content exists in the DOM,
causing the cross-fade to reveal a blank or partial page.
-->
<link rel="expect" href="#main-content" blocking="render">
</head>
<body>
<header>...</header>
<main id="main-content">
<h1>Page Title</h1>
<p>Above-the-fold content the user should see immediately.</p>
</main>
<!-- Content below the fold does NOT need to be blocked on -->
<section>...</section>
</body>
กรณีการใช้งานที่ 2: เปลี่ยนรูปแบบภาพเคลื่อนไหวระหว่างองค์ประกอบที่เฉพาะเจาะจง
เมื่อองค์ประกอบในทั้ง 2 หน้าแชร์ view-transition-name เบราว์เซอร์จะเปลี่ยนรูปแบบองค์ประกอบเหล่านั้นอย่างราบรื่นตลอดการนำทาง หากองค์ประกอบเป้าหมายยังไม่ได้รับการแยกวิเคราะห์เมื่อการเปลี่ยนเริ่มต้นขึ้น เบราว์เซอร์จะไม่พบองค์ประกอบดังกล่าว การเปลี่ยนรูปแบบจะลดระดับลงเป็นการแยกภาพเคลื่อนไหวทางออกและทางเข้า บล็อกการแสดงผลจนกว่าจะแยกวิเคราะห์องค์ประกอบที่มี view-transition-name แล้ว
เนื่องจาก <link rel="expect"> จะหยุดการแสดงผลชั่วคราวจนกว่าจะมีการแยกวิเคราะห์องค์ประกอบ DOM เป้าหมาย และไม่รอให้ข้อมูลรูปภาพดาวน์โหลดหรือถอดรหัส ระบบจึงอาจจับภาพ <img> ที่ไม่ได้แคชเป็นสี่เหลี่ยมผืนผ้าว่างเปล่าในภาพรวมของหน้าใหม่ เพื่อป้องกันไม่ให้เกิดภาพรวมที่ว่างเปล่าหรือการเปลี่ยนเลย์เอาต์ระหว่างการเปลี่ยนรูปแบบรูปภาพ ให้โหลดล่วงหน้าหรือถอดรหัสรูปภาพการเปลี่ยนผ่านที่สำคัญล่วงหน้า (เช่น ด้วย <link rel="preload" as="image"> หรือโดยการแสดงผลหน้าปลายทางล่วงหน้า) และระบุขนาดสำรองที่ชัดเจน (แอตทริบิวต์ width และ height หรือ CSS aspect-ratio รวมถึงสีพื้นหลังของตัวยึดตำแหน่ง)
<head>
<link rel="stylesheet" href="/css/styles.css">
<!--
DO: Preload critical transition images so their pixels are
available to decode before the new-page snapshot is captured.
<link rel="expect"> only waits for the DOM element to be parsed,
NOT for image pixels to download or decode.
-->
<link rel="preload" as="image" href="/img/product.webp" fetchpriority="high">
<!--
DO: Block rendering until the element participating in the
morph animation has been parsed. Without this, the browser
may start the transition before #hero exists, causing the
morph to degrade to a fade-out/fade-in.
-->
<link rel="expect" href="#hero" blocking="render">
<!--
When multiple blocking="render" resources are present,
rendering is blocked until ALL of them are satisfied.
Here, the browser waits for both the script to execute
AND the #hero element to be parsed — whichever comes last.
-->
<script async blocking="render" src="/js/transition-setup.js"></script>
</head>
<body>
<header>...</header>
<section id="hero">
<h1 style="view-transition-name: page-title">Product Name</h1>
<!--
DO: Provide explicit dimensions (width/height or aspect-ratio)
as a sizing fallback so the snapshot captures the correct layout
box even if image decoding is still in progress.
-->
<img
style="view-transition-name: hero-image"
src="/img/product.webp"
width="800"
height="600"
alt="Product"
>
</section>
</body>
ขั้นตอนที่ 4: ใช้ Media Query สำหรับการบล็อกการแสดงผลที่ปรับเปลี่ยนตามอุปกรณ์
วิวพอร์ตขนาดต่างๆ อาจแสดงเนื้อหาในส่วนครึ่งหน้าบนในปริมาณที่แตกต่างกัน ใช้แอตทริบิวต์ media ใน <link rel="expect"> เพื่อบล็อกการแสดงผลเฉพาะเนื้อหาที่มองเห็นได้ที่ความกว้างของวิวพอร์ตที่กำหนด
<head>
<!--
DO: Use media queries to conditionally block rendering.
On wide screens, both the hero and the sidebar are visible,
so block until both are parsed. On narrow screens, only the
hero is visible initially.
-->
<link
rel="expect"
href="#hero"
blocking="render"
media="screen and (width <= 768px)"
>
<link
rel="expect"
href="#sidebar"
blocking="render"
media="screen and (width > 768px)"
>
</head>
ขั้นตอนที่ 5: ใช้ pagereveal สำหรับการเปลี่ยนที่ขึ้นอยู่กับบริบท (ไม่บังคับ)
เหตุการณ์ pagereveal ไม่จำเป็นสำหรับกลยุทธ์การบล็อกการแสดงผลหลัก จำเป็นต้องใช้เฉพาะเมื่อต้องกำหนดค่า view-transition-name แบบไดนามิกตามตำแหน่งที่ผู้ใช้ไปยังส่วนต่างๆ เช่น การเปลี่ยนรูปแบบรายการที่เฉพาะเจาะจงเป็นส่วนหัวของหน้ารายละเอียด
หากมีการกำหนดค่า view-transition-name แบบคงที่ใน CSS หรือหากคุณใช้เฉพาะการเฟดข้ามแบบเต็มหน้าเริ่มต้น ให้ข้ามขั้นตอนนี้ไปเลย
<head>
<!--
MANDATORY: The pagereveal listener must be registered before
the page renders. Inline small setup scripts with blocking="render"
to avoid an extra network round-trip on cold loads, or use an
external async script with blocking="render" so the listener is
registered early without blocking HTML parsing.
If the listener is registered too late (e.g., in a deferred
script), the event may have already fired.
-->
<script async blocking="render" src="/js/transition-setup.js"></script>
</head>
// transition-setup.js
window.addEventListener('pagereveal', async (event) => {
if (!event.viewTransition) return;
const from = navigation.activation?.from;
if (!from) return;
const fromUrl = new URL(from.url);
// DO: Assign view-transition-name based on navigation context.
// This enables a morph animation from the product card on the
// list page to the heading on the detail page.
if (fromUrl.pathname === '/products/') {
const heading = document.querySelector('main h1');
if (!heading) return;
heading.style.viewTransitionName = 'product-title';
try {
await event.viewTransition.ready;
} catch (error) {
// Skipped view transitions reject `ready` with InvalidStateError.
if (error.name !== 'InvalidStateError') {
throw error;
}
} finally {
// MANDATORY: Remove the temporary name after the transition
// finishes. Stale names interfere with subsequent navigations
// and prevent the page from entering the bfcache.
await event.viewTransition.finished;
heading.style.viewTransitionName = '';
}
}
});
แนวทางปฏิบัติแนะนำ
- ควรกำหนด
view-transition-nameผ่าน CSS ทุกครั้งที่ทำได้ สงวนการกำหนด JavaScript (ผ่านpagereveal) ไว้สำหรับกรณีที่ชื่อขึ้นอยู่กับบริบทการนำทาง - ควรทำให้สคริปต์ที่บล็อกการแสดงผลมีขนาดเล็กและทำงานได้รวดเร็ว เบราว์เซอร์มีระยะหมดเวลาในตัว (ประมาณ 4 วินาที) หลังจากนั้นระบบจะข้ามการเปลี่ยนผ่านทั้งหมด (ปฏิเสธ
viewTransition.readyด้วยInvalidStateErrorหากมีการเริ่มการเปลี่ยนผ่าน) - อย่าใช้
<link rel="expect">เพื่อบล็อกองค์ประกอบที่อยู่ลึกเข้าไปในหน้าเว็บซึ่งมองไม่เห็นในวิวพอร์ตเริ่มต้น ซึ่งจะทำให้การเปลี่ยนภาพล่าช้าโดยไม่มีประโยชน์ด้านภาพ - อย่าทำเครื่องหมายตัวโหลดการวิเคราะห์ เครือข่ายโฆษณา เครื่องมือจัดการแท็ก หรือตัวโหลดของบุคคลที่สามอื่นๆ ด้วย
blocking="render"แม้ว่าจะแทรกเนื้อหาลงในวิวพอร์ตเริ่มต้นก็ตาม เวลาในการตอบสนองของเครือข่ายจะนับรวมในระยะหมดเวลาของการเปลี่ยนสถานะ และเอาต์พุตจะไม่เป็นส่วนหนึ่งของสถานะที่เสถียรของหน้าเว็บ - อย่ากำหนด
view-transition-nameเดียวกันให้กับองค์ประกอบหลายรายการในหน้าเดียวกัน ชื่อที่ซ้ำกันจะทำให้ระบบข้ามการเปลี่ยนทั้งหมด - ผลกระทบด้านเวลาของเทคโนโลยีความช่วยเหลือพิเศษ: การใช้
blocking="render"จะทำให้การอัปเดตภาพและการแสดงผลครั้งแรกล่าช้า แม้ว่าวิธีนี้จะช่วยป้องกันข้อบกพร่องด้านภาพสำหรับผู้ใช้ที่มองเห็นได้ แต่ก็อาจทำให้เกิดเวลาในการตอบสนองของการประมวลผลหรือการเริ่มต้นที่ล่าช้าสำหรับโปรแกรมอ่านหน้าจอและเทคโนโลยีความช่วยเหลือพิเศษอื่นๆ ที่ขึ้นอยู่กับแผนผังการช่วยเหลือพิเศษที่แสดงผล พิจารณาข้อดีของความต่อเนื่องของภาพเทียบกับเวลาในการตอบสนองในการอ่านครั้งแรกสำหรับผู้ใช้ที่ไม่ใช่ภาพ และตรวจสอบว่าสคริปต์ที่บล็อกการแสดงผลมีจำนวนน้อยที่สุดและได้รับการเพิ่มประสิทธิภาพอย่างมาก
กลยุทธ์สำรอง
การเปลี่ยนมุมมองข้ามเอกสารเป็นตัวเลือกที่ยอดเยี่ยมสำหรับการเพิ่มประสิทธิภาพแบบต่อเนื่อง ในเบราว์เซอร์ที่ไม่รองรับ ระบบจะไม่สนใจกฎ @view-transition และการนําทางแบบต้นทางเดียวกันมาตรฐานจะเกิดขึ้นเหมือนกับที่เกิดขึ้นโดยไม่มีฟีเจอร์นี้ เบราว์เซอร์ที่รองรับจะมีการเปลี่ยนภาพที่ราบรื่น ส่วนเบราว์เซอร์อื่นๆ ทั้งหมดจะมีการนำทางแบบมาตรฐาน การรองรับเบราว์เซอร์ที่จำกัดไม่ใช่เหตุผลที่จะหลีกเลี่ยงการนำไปใช้
เบราว์เซอร์ทั้งหมดที่รองรับการเปลี่ยนมุมมองข้ามเอกสารยังรองรับ blocking="render" และ <link rel="expect"> ด้วย ดังนั้นจึงไม่จำเป็นต้องใช้การสำรองแยกต่างหากสำหรับฟีเจอร์การบล็อกการแสดงผลที่อธิบายไว้ในคู่มือนี้
ปัจจัยอื่นๆ ที่ควรพิจารณา
- ผลกระทบต่อประสิทธิภาพ: ทรัพยากรที่บล็อกการแสดงผลทุกรายการจะทำให้ภาพเคลื่อนไหวการเปลี่ยนมุมมองเริ่มช้าลง ลดจำนวนสคริปต์ที่บล็อกการแสดงผลและใช้
<link rel="expect">สำหรับองค์ประกอบที่อยู่ครึ่งหน้าบนเท่านั้น แสดงผลหน้าปลายทางล่วงหน้าโดยใช้ Speculation Rules API เพื่อลดความล่าช้าในการโหลดทั้งหมด - ลักษณะการทำงานเมื่อหมดเวลา: หากเวลาที่ใช้ในการบล็อกการแสดงผลรวมกันเกินประมาณ 4 วินาที เบราว์เซอร์จะข้ามการเปลี่ยนผ่าน (และสัญญา
viewTransition.readyที่ใช้งานอยู่จะปฏิเสธด้วยInvalidStateError) ตรวจสอบว่าทรัพยากรที่สำคัญโหลดได้ดีภายในกรอบเวลานี้ - ความเข้ากันได้ของ bfcache: การกำหนด
view-transition-nameชั่วคราวที่ไม่ได้ล้างหลังจากการเปลี่ยนผ่านอาจทำให้หน้าเว็บเข้าสู่ bfcache ไม่ได้ นำชื่อที่กำหนดแบบไดนามิกใน Callback ของfinishedออกเสมอ