เราทราบดีว่าการตอบสนองต่อการเลื่อนเป็นสิ่งสำคัญต่อการมีส่วนร่วมของผู้ใช้กับเว็บไซต์บนอุปกรณ์เคลื่อนที่ แต่โปรแกรมรับฟังเหตุการณ์การสัมผัสมักทำให้เกิดปัญหาด้านประสิทธิภาพการเลื่อนอย่างร้ายแรง Chrome แก้ไขปัญหานี้ด้วยการอนุญาตให้ใช้ passive (ส่งตัวเลือก {passive: true}
ไปยัง addEventListener()
) กับ pointer events API
ฟีเจอร์เหล่านี้เป็นฟีเจอร์ที่ยอดเยี่ยมในการขับเคลื่อนเนื้อหาใหม่ไปยังรูปแบบที่ไม่บล็อกการเลื่อน แต่บางครั้งนักพัฒนาแอปอาจเข้าใจและนำไปใช้ยาก
เราเชื่อว่าเว็บควรจะทำงานได้อย่างรวดเร็วโดยค่าเริ่มต้นโดยไม่ต้องให้นักพัฒนาซอฟต์แวร์เข้าใจรายละเอียดที่ซับซ้อนของลักษณะการทํางานของเบราว์เซอร์ ใน Chrome 56 เราจะตั้งค่าListener ของเหตุการณ์การแตะเป็นแบบแพสซีฟโดยค่าเริ่มต้น ในกรณีที่ตรงกับความต้องการของนักพัฒนาซอฟต์แวร์มากที่สุด เราเชื่อว่าการดําเนินการนี้จะช่วยปรับปรุงประสบการณ์ของผู้ใช้ได้อย่างมาก ทั้งยังลดผลกระทบเชิงลบต่อเว็บไซต์ให้เหลือน้อยที่สุด
ในบางกรณีที่ไม่ได้เกิดขึ้นบ่อย การเปลี่ยนแปลงนี้อาจส่งผลให้เกิดการเลื่อนโดยไม่ตั้งใจ ปัญหานี้มักจะแก้ไขได้ง่ายๆ โดยใช้สไตล์ touch-action: none กับองค์ประกอบที่ไม่ควรมีการเลื่อน อ่านรายละเอียดต่อเพื่อดูวิธีตรวจสอบว่าคุณได้รับผลกระทบหรือไม่ และสิ่งที่ทำได้
เบื้องหลัง: เหตุการณ์ที่ยกเลิกได้ทําให้หน้าเว็บช้าลง
หากคุณเรียกใช้ preventDefault() ในเหตุการณ์ touchstart
หรือ touchmove
รายการแรก จะเป็นการป้องกันการเลื่อน
ปัญหาคือส่วนใหญ่ Listeners จะไม่เรียก preventDefault()
แต่เบราว์เซอร์ต้องรอให้เหตุการณ์เสร็จสิ้นจึงจะแน่ใจได้
"Listener เหตุการณ์แบบแพสซีฟ" ที่นักพัฒนาแอปกำหนดจะแก้ปัญหานี้ได้ เมื่อคุณเพิ่มเหตุการณ์การแตะที่มีออบเจ็กต์ {passive: true}
เป็นพารามิเตอร์ที่ 3 ในตัวแฮนเดิลเหตุการณ์ แสดงว่าคุณกำลังบอกเบราว์เซอร์ว่าตัวฟัง touchstart
จะไม่เรียก preventDefault()
และเบราว์เซอร์สามารถเลื่อนได้อย่างปลอดภัยโดยไม่ต้องบล็อกตัวฟัง เช่น
window.addEventListener("touchstart", func, {passive: true} );
The Intervention
แรงจูงใจหลักของเราคือการลดเวลาในการอัปเดตการแสดงผลหลังจากที่ผู้ใช้สัมผัสหน้าจอ เพื่อทำความเข้าใจการใช้งาน touchstart และ touchmove เราจึงเพิ่มเมตริกเพื่อระบุความถี่ที่ลักษณะการบล็อกการเลื่อนเกิดขึ้น
เราพิจารณาเปอร์เซ็นต์ของเหตุการณ์การแตะแบบยกเลิกได้ซึ่งส่งไปยังเป้าหมายรูท (หน้าต่าง เอกสาร หรือบอดี้) และพบว่า Listener ประมาณ 80% เหล่านี้เป็นแบบพาสซีฟตามแนวคิด แต่ไม่ได้ลงทะเบียนเป็นเช่นนั้น เมื่อพิจารณาถึงขนาดของปัญหานี้ เราจึงเห็นโอกาสอันยอดเยี่ยมในการปรับปรุงการเลื่อนโดยที่นักพัฒนาแอปไม่ต้องดำเนินการใดๆ โดยการทําให้เหตุการณ์เหล่านี้ "ไม่ได้อยู่ในสถานะใช้งาน" โดยอัตโนมัติ
ด้วยเหตุนี้ เราจึงกำหนดการแทรกแซงของเราว่า: หากเป้าหมายของ Listener ของ touchstart หรือ
touchmove คือ window
, document
หรือ body
เราจะตั้งค่าเริ่มต้น
passive
เป็น true
ซึ่งหมายความว่าโค้ดอย่างเช่น
window.addEventListener("touchstart", func);
จะกลายเป็น
window.addEventListener("touchstart", func, {passive: true} );
ตอนนี้การเรียก preventDefault()
ภายใน Listener จะถูกละเว้น
กราฟด้านล่างแสดงเวลาที่ใช้ในการเลื่อน 1% อันดับสูงสุดนับจากเวลาที่ผู้ใช้แตะหน้าจอเพื่อเลื่อนไปจนถึงเวลาที่หน้าจออัปเดต ข้อมูลนี้ใช้กับทุกเว็บไซต์ใน Chrome สำหรับ Android ก่อนที่จะเปิดใช้การแทรกแซง การเลื่อน 1% ใช้เวลาเพียง 400 มิลลิวินาที ตอนนี้ลดลงเหลือเพียง 250 มิลลิวินาทีใน Chrome 56 เบต้า ซึ่งลดลงประมาณ 38% ในอนาคตเราหวังว่าจะทำให้ตัวเลือก passive true เป็นค่าเริ่มต้นสำหรับทั้งหมดของ touchstart
และ touchmove
Listeners เพื่อลดเวลานี้ให้เหลือต่ำกว่า 50 มิลลิวินาที

การแตกหักและคำแนะนำ
ในกรณีส่วนใหญ่ จะไม่มีความเสียหายเกิดขึ้น แต่หากเกิดข้อขัดข้องขึ้น อาการที่พบบ่อยที่สุดคือระบบจะเลื่อนไปโดยที่คุณไม่ต้องการ ในบางกรณีที่เกิดขึ้นไม่บ่อยนัก นักพัฒนาซอฟต์แวร์อาจเห็นเหตุการณ์การคลิกที่ไม่คาดคิดเช่นกัน (เมื่อไม่มี preventDefault()
ใน touchend
Listener)
ใน Chrome 56 ขึ้นไป DevTools จะบันทึกคำเตือนเมื่อคุณเรียกใช้
preventDefault()
ในเหตุการณ์ที่มีการแทรกแซง
touch-passive.html:19 Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080
แอปพลิเคชันสามารถระบุได้ว่าอาจพบปัญหานี้ในการใช้งานจริงหรือไม่โดยตรวจสอบว่าการเรียก preventDefault
มีผลใดๆ ผ่านพร็อพเพอร์ตี้ defaultPrevented
หรือไม่
เราพบว่าหน้าเว็บส่วนใหญ่ที่ได้รับผลกระทบแก้ไขได้ง่ายโดยใช้พร็อพเพอร์ตี้ CSS touch-action ทุกครั้งที่เป็นไปได้ หากต้องการป้องกันไม่ให้เบราว์เซอร์เลื่อนและซูมภายในองค์ประกอบทั้งหมด ให้ใช้ touch-action: none
กับองค์ประกอบนั้น หากคุณมีภาพสไลด์แนวนอน ให้ลองใช้ touch-action: pan-y pinch-zoom
กับภาพสไลด์ดังกล่าวเพื่อให้ผู้ใช้ยังเลื่อนในแนวตั้งและซูมได้ตามปกติ การใช้แอตทริบิวต์ touch-action อย่างถูกต้องเป็นสิ่งจําเป็นอยู่แล้วในเบราว์เซอร์ เช่น Edge บนเดสก์ท็อป ที่รองรับเหตุการณ์ Pointer แต่ไม่ใช่เหตุการณ์ Touch สำหรับ Safari บนอุปกรณ์เคลื่อนที่และเบราว์เซอร์บนอุปกรณ์เคลื่อนที่รุ่นเก่าที่ไม่รองรับการแตะเพื่อดำเนินการ โปรแกรมฟังการแตะของคุณต้องเรียกใช้ preventDefault
ต่อไปแม้ว่า Chrome จะละเว้นก็ตาม
ในกรณีที่ซับซ้อนมากขึ้น คุณอาจต้องใช้วิธีใดวิธีหนึ่งต่อไปนี้ด้วย
- หาก
touchstart
listener เรียกpreventDefault()
ให้ตรวจสอบว่ามีการเรียก preventDefault() จาก Listener ของ touchend ที่เชื่อมโยงด้วยเพื่อระงับการสร้างเหตุการณ์การคลิกและลักษณะการแตะเริ่มต้นอื่นๆ ต่อไป - สุดท้าย (และไม่ควรทำ) ให้ส่ง
{passive: false}
ไปยัง addEventListener() เพื่อลบล้างลักษณะการทำงานเริ่มต้น โปรดทราบว่าคุณจะต้องตรวจหาฟีเจอร์ว่า User Agent รองรับ EventListenerOptions หรือไม่
บทสรุป
ใน Chrome 56 การเลื่อนในหลายเว็บไซต์จะเริ่มต้นเร็วขึ้นอย่างมาก นี่เป็นผลกระทบเพียงอย่างเดียวที่นักพัฒนาแอปส่วนใหญ่จะสังเกตเห็นจากการเปลี่ยนแปลงนี้ ในบางกรณี นักพัฒนาแอปอาจสังเกตเห็นการเลื่อนโดยไม่ตั้งใจ
แม้ว่าจะยังคงจําเป็นต้องทำเช่นนั้นสําหรับ Safari บนอุปกรณ์เคลื่อนที่ แต่เว็บไซต์ไม่ควรพึ่งพาการเรียก preventDefault()
ภายใน touchstart
และ touchmove
Listeners เนื่องจาก Chrome ไม่รับประกันว่าจะดำเนินการดังกล่าวอีกต่อไป นักพัฒนาซอฟต์แวร์ควรใช้พร็อพเพอร์ตี้ touch-action
CSS ในองค์ประกอบที่ควรปิดใช้การเลื่อนและการซูมเพื่อแจ้งให้เบราว์เซอร์ทราบก่อนที่จะเกิดเหตุการณ์การสัมผัส
หากต้องการระงับลักษณะการทํางานเริ่มต้นของการแตะ (เช่น การสร้างเหตุการณ์การคลิก) ให้เรียกใช้ preventDefault()
ภายใน touchend
Listener