ResizeObserver จะแจ้งให้คุณทราบเมื่อขนาดขององค์ประกอบมีการเปลี่ยนแปลง
ก่อนหน้านี้ResizeObserver คุณต้องแนบ Listener ไปยังresize
เหตุการณ์ของเอกสารเพื่อรับการแจ้งเตือนเกี่ยวกับการเปลี่ยนแปลงขนาดของ Viewport ในตัวแฮนเดิลเหตุการณ์ คุณจะต้องพิจารณาว่าองค์ประกอบใดได้รับผลกระทบจากการเปลี่ยนแปลงนั้น และเรียกใช้กิจวัตรเฉพาะเพื่อตอบสนองอย่างเหมาะสม หากต้องการทราบขนาดใหม่ขององค์ประกอบหลังจากปรับขนาด คุณจะต้องเรียกใช้ getBoundingClientRect() หรือ getComputedStyle() ซึ่งอาจทำให้เลย์เอาต์เกิดการ Thrash หากคุณไม่ระมัดระวังในการจัดกลุ่มการอ่านทั้งหมดและการเขียนทั้งหมด
ซึ่งยังไม่รวมถึงกรณีที่องค์ประกอบเปลี่ยนขนาดโดยที่ไม่ได้ปรับขนาดหน้าต่างหลัก ตัวอย่างเช่น การเพิ่มองค์ประกอบย่อยใหม่ การตั้งค่าdisplayสไตล์ขององค์ประกอบเป็น none หรือการดำเนินการที่คล้ายกันอาจเปลี่ยนขนาดขององค์ประกอบ องค์ประกอบที่อยู่ระดับเดียวกัน หรือองค์ประกอบหลัก
ด้วยเหตุนี้ ResizeObserver จึงเป็นองค์ประกอบพื้นฐานที่มีประโยชน์ โดยจะตอบสนองต่อการเปลี่ยนแปลง
ขนาดขององค์ประกอบที่สังเกตได้ ไม่ว่าสาเหตุของการเปลี่ยนแปลงนั้นคืออะไรก็ตาม
นอกจากนี้ยังให้สิทธิ์เข้าถึงขนาดใหม่ขององค์ประกอบที่สังเกตได้ด้วย
API
API ทั้งหมดที่มีคำต่อท้าย Observer ตามที่เรากล่าวถึงข้างต้นใช้การออกแบบ API ที่เรียบง่าย
ResizeObserver ก็เช่นกัน คุณสร้างออบเจ็กต์ ResizeObserver และส่ง Callback ไปยังเครื่องมือสร้าง ระบบจะส่งอาร์เรย์ของออบเจ็กต์ ResizeObserverEntry ไปยังฟังก์ชันเรียกกลับ ซึ่งมีรายการ 1 รายการต่อองค์ประกอบที่สังเกตการณ์ และมีขนาดใหม่ขององค์ประกอบ
var ro = new ResizeObserver(entries => {
for (let entry of entries) {
const cr = entry.contentRect;
console.log('Element:', entry.target);
console.log(`Element size: ${cr.width}px x ${cr.height}px`);
console.log(`Element padding: ${cr.top}px ; ${cr.left}px`);
}
});
// Observe one or multiple elements
ro.observe(someElement);
รายละเอียดบางส่วน
มีการรายงานอะไร
โดยทั่วไปแล้ว ResizeObserverEntry จะรายงานกล่องเนื้อหาขององค์ประกอบผ่านพร็อพเพอร์ตี้ที่ชื่อ contentRect ซึ่งจะแสดงผลออบเจ็กต์ DOMRectReadOnly กล่องเนื้อหาคือกล่องที่วางเนื้อหาได้ ซึ่งก็คือ
กล่องเส้นขอบลบด้วยระยะขอบ
โปรดทราบว่าแม้ว่าResizeObserver รายงานทั้งมิติข้อมูล
ของcontentRectและการเว้นวรรค แต่จะดูเฉพาะcontentRect
อย่าสับสนระหว่าง contentRect กับกรอบล้อมรอบขององค์ประกอบ กรอบล้อม
ตามที่ getBoundingClientRect() รายงานคือกรอบที่ประกอบด้วย
องค์ประกอบทั้งหมดและองค์ประกอบย่อย SVG เป็นข้อยกเว้นของกฎ โดยResizeObserverจะรายงานขนาดของกรอบล้อมรอบ
ตั้งแต่ Chrome 84 เป็นต้นไป ResizeObserverEntry จะมีพร็อพเพอร์ตี้ใหม่ 3 รายการเพื่อให้ข้อมูลที่ละเอียดยิ่งขึ้น
พร็อพเพอร์ตี้แต่ละรายการจะแสดงออบเจ็กต์ ResizeObserverSize
ที่มีพร็อพเพอร์ตี้ blockSize และพร็อพเพอร์ตี้ inlineSize ข้อมูลนี้เป็นข้อมูลเกี่ยวกับองค์ประกอบที่สังเกตได้ ณ เวลาที่เรียกใช้ Callback
borderBoxSizecontentBoxSizedevicePixelContentBoxSize
รายการทั้งหมดนี้จะแสดงอาร์เรย์แบบอ่านอย่างเดียว เนื่องจากในอนาคตเราหวังว่า รายการเหล่านี้จะรองรับองค์ประกอบที่มีหลายส่วน ซึ่งเกิดขึ้นใน สถานการณ์แบบหลายคอลัมน์ ขณะนี้อาร์เรย์เหล่านี้จะมีองค์ประกอบเพียงรายการเดียว
แพลตฟอร์มรองรับพร็อพเพอร์ตี้เหล่านี้อย่างจำกัด แต่ Firefox รองรับ 2 รายการแรกอยู่แล้ว
มีการรายงานเมื่อใด
ข้อกำหนดระบุว่า ResizeObserver ควรประมวลผลเหตุการณ์การปรับขนาดทั้งหมด
ก่อนการแสดงผลและหลังเลย์เอาต์ ซึ่งทำให้การเรียกกลับของ ResizeObserver เป็น
ตำแหน่งที่เหมาะที่สุดในการเปลี่ยนแปลงเลย์เอาต์ของหน้า เนื่องจากResizeObserver
การประมวลผลเกิดขึ้นระหว่างเลย์เอาต์และการแสดงผล การทำเช่นนี้จะทำให้เลย์เอาต์ไม่ถูกต้องเท่านั้น
ไม่ใช่การแสดงผล
รับทราบ
คุณอาจสงสัยว่าจะเป็นอย่างไรหากเปลี่ยนขนาดขององค์ประกอบที่สังเกตการณ์
ภายในโค้ดเรียกกลับเป็น ResizeObserver คำตอบคือคุณจะทริกเกอร์
การเรียกกลับอีกครั้งไปยังฟังก์ชันเรียกกลับทันที โชคดีที่ ResizeObserver มี
กลไกเพื่อหลีกเลี่ยงการวนซ้ำของฟังก์ชันเรียกกลับแบบไม่มีที่สิ้นสุดและการขึ้นต่อกันแบบวงจร ระบบจะประมวลผลการเปลี่ยนแปลงในเฟรมเดียวกันเท่านั้น หากองค์ประกอบที่ปรับขนาดอยู่ลึกลงไปในโครงสร้าง DOM
มากกว่าองค์ประกอบตื้นที่สุดที่ประมวลผลใน Callback ก่อนหน้า
มิเช่นนั้น ระบบจะเลื่อนการแสดงผลไปยังเฟรมถัดไป
แอปพลิเคชัน
สิ่งหนึ่งที่ ResizeObserver ช่วยให้คุณทำได้คือการใช้การค้นหาสื่อต่อองค์ประกอบ
การสังเกตองค์ประกอบช่วยให้คุณกำหนดจุดพักการออกแบบและเปลี่ยนรูปแบบขององค์ประกอบได้ ในตัวอย่างต่อไปนี้ กล่องที่ 2
จะเปลี่ยนรัศมีเส้นขอบตามความกว้าง
const ro = new ResizeObserver(entries => {
for (let entry of entries) {
entry.target.style.borderRadius =
Math.max(0, 250 - entry.contentRect.width) + 'px';
}
});
// Only observe the second box
ro.observe(document.querySelector('.box:nth-child(2)'));
อีกตัวอย่างที่น่าสนใจคือหน้าต่างแชท ปัญหาที่เกิดขึ้น ในเลย์เอาต์การสนทนาจากบนลงล่างทั่วไปคือการจัดตำแหน่งการเลื่อน เพื่อไม่ให้ผู้ใช้สับสน คุณควรให้หน้าต่างติดอยู่ที่ด้านล่างของการสนทนา ซึ่งเป็นที่ที่ข้อความล่าสุดปรากฏ นอกจากนี้ การเปลี่ยนเลย์เอาต์ ทุกประเภท (เช่น การเปลี่ยนจากแนวนอนเป็นแนวตั้งหรือในทางกลับกัน) ควร ให้ผลลัพธ์เดียวกัน
ResizeObserver ช่วยให้คุณเขียนโค้ดชิ้นเดียวที่จัดการกับสถานการณ์ทั้ง 2 แบบได้ การปรับขนาดหน้าต่างเป็นเหตุการณ์ที่ ResizeObserver สามารถ
บันทึกได้ตามคำจำกัดความ แต่การเรียกใช้ appendChild() จะปรับขนาดองค์ประกอบนั้นด้วย
(เว้นแต่จะตั้งค่า overflow: hidden) เนื่องจากต้องเว้นที่ว่างสำหรับองค์ประกอบใหม่
ด้วยเหตุนี้ จึงใช้โค้ดเพียงไม่กี่บรรทัดเพื่อให้ได้เอฟเฟกต์ที่ต้องการ
const ro = new ResizeObserver(entries => {
document.scrollingElement.scrollTop =
document.scrollingElement.scrollHeight;
});
// Observe the scrollingElement for when the window gets resized
ro.observe(document.scrollingElement);
// Observe the timeline to process new messages
ro.observe(timeline);
เจ๋งใช่ไหมล่ะ
จากตรงนี้ ฉันสามารถเพิ่มโค้ดเพื่อจัดการกรณีที่ผู้ใช้เลื่อนขึ้นด้วยตนเองและต้องการให้การเลื่อนติดอยู่กับข้อความนั้นเมื่อมีข้อความใหม่เข้ามา
อีกกรณีการใช้งานคือสำหรับองค์ประกอบที่กำหนดเองทุกประเภทที่ทำการจัดเลย์เอาต์ของตัวเอง
จนถึง ResizeObserver ไม่มีวิธีที่เชื่อถือได้ในการรับการแจ้งเตือนเมื่อขนาดขององค์ประกอบเปลี่ยนแปลง เพื่อให้วางองค์ประกอบย่อยได้อีกครั้ง
ผลกระทบต่อ Interaction to Next Paint (INP)
Interaction to Next Paint (INP) เป็นเมตริกที่วัด การตอบสนองโดยรวมของหน้าเว็บต่อการโต้ตอบของผู้ใช้ หากค่า INP ของหน้าเว็บอยู่ในเกณฑ์ "ดี" ซึ่งก็คือไม่เกิน 200 มิลลิวินาที แสดงว่าหน้าเว็บตอบสนองต่อการโต้ตอบของผู้ใช้ได้อย่างน่าเชื่อถือ
แม้ว่าระยะเวลาที่ใช้ในการเรียกกลับของเหตุการณ์เพื่อตอบสนองต่อ การโต้ตอบของผู้ใช้จะมีส่วนสำคัญต่อเวลาในการตอบสนองทั้งหมดของการโต้ตอบ แต่ก็ไม่ใช่แง่มุมเดียวของ INP ที่ต้องพิจารณา นอกจากนี้ INP ยังพิจารณาจำนวน เวลาที่ใช้ในการเกิด Next Paint ของการโต้ตอบด้วย ซึ่งเป็น ระยะเวลาที่ใช้ในการแสดงผลที่จำเป็นต่อการอัปเดตส่วนติดต่อผู้ใช้ เพื่อตอบสนองต่อการโต้ตอบให้เสร็จสมบูรณ์
ในส่วนของ ResizeObserver นั้น การดำเนินการนี้มีความสำคัญเนื่องจาก Callback ที่อินสแตนซ์ ResizerObserver เรียกใช้จะเกิดขึ้นก่อนการทำงานของการแสดงผล การทำงานนี้เป็นไปตามที่ออกแบบไว้ เนื่องจากต้องคำนึงถึงงานที่เกิดขึ้นใน Callback และผลลัพธ์ของงานนั้นมีแนวโน้มสูงที่จะต้องมีการเปลี่ยนแปลงในอินเทอร์เฟซผู้ใช้
โปรดระมัดระวังอย่าให้มีการทำงานด้านการแสดงผลมากเกินความจำเป็นในResizeObserver
การเรียกกลับ เนื่องจากงานด้านการแสดงผลที่มากเกินไปอาจทำให้เบราว์เซอร์
ทำงานสำคัญได้ช้า เช่น หากการโต้ตอบใดๆ มี
การเรียกกลับที่ทำให้ResizeObserverการเรียกกลับทำงาน ให้ตรวจสอบว่าคุณได้ทำ
สิ่งต่อไปนี้เพื่อให้ได้รับประสบการณ์การใช้งานที่ราบรื่นที่สุด
- ตรวจสอบว่าตัวเลือก CSS นั้นเรียบง่ายที่สุดเท่าที่จะเป็นไปได้เพื่อหลีกเลี่ยง การคำนวณรูปแบบใหม่มากเกินไป การคำนวณรูปแบบใหม่จะเกิดขึ้นก่อนเลย์เอาต์ และตัวเลือก CSS ที่ซับซ้อนอาจ ทำให้การดำเนินการเลย์เอาต์ล่าช้า
- หลีกเลี่ยงการทำงานในโค้ดเรียกกลับของ
ResizeObserverที่อาจทําให้เกิดการจัดวางใหม่แบบบังคับ - โดยทั่วไปแล้ว เวลาที่ต้องใช้ในการอัปเดตเลย์เอาต์ของหน้าจะเพิ่มขึ้นตาม
จำนวนองค์ประกอบ DOM ในหน้า แม้ว่าจะเป็นจริงไม่ว่าหน้าเว็บจะใช้
ResizeObserverหรือไม่ก็ตาม แต่การทำงานในแฮนเดิลResizeObserverอาจมีความสำคัญมากขึ้นเมื่อความซับซ้อนเชิงโครงสร้างของหน้าเว็บเพิ่มขึ้น
บทสรุป
ResizeObserver พร้อมใช้งานในเบราว์เซอร์หลักทั้งหมด
และเป็นวิธีที่มีประสิทธิภาพในการตรวจสอบการปรับขนาดองค์ประกอบที่ระดับองค์ประกอบ แต่โปรดระวังอย่าหน่วงเวลาการแสดงผลมากเกินไปด้วย API ที่มีประสิทธิภาพนี้