ทำให้การเลื่อนด้วยการแตะทำงานเร็วโดยค่าเริ่มต้น

เดฟ ตาปูสกา
Dave Tapuska

เราทราบดีว่าการตอบสนองของการเลื่อนมีความสำคัญต่อการมีส่วนร่วมของผู้ใช้กับเว็บไซต์ในอุปกรณ์เคลื่อนที่ แต่ Listener เหตุการณ์แบบสัมผัสมักจะทำให้เกิดปัญหาที่ส่งผลต่อประสิทธิภาพการเลื่อนที่ร้ายแรง Chrome ได้แก้ไขปัญหานี้โดยอนุญาตให้ Listener เหตุการณ์การแตะเป็นแบบ แพสซีฟ (ส่งตัวเลือก {passive: true} ไปยัง addEventListener()) และจัดส่ง เหตุการณ์ตัวชี้ API ฟีเจอร์เหล่านี้เป็นฟีเจอร์ที่ยอดเยี่ยมในการกระตุ้นเนื้อหาใหม่ให้เป็นโมเดลที่ไม่บล็อกการเลื่อน แต่ในบางครั้งนักพัฒนาซอฟต์แวร์พบว่าเนื้อหาเหล่านี้เข้าใจยากและนำไปใช้ได้ยาก

เราเชื่อว่าเว็บควรจะเร็วโดยค่าเริ่มต้นโดยที่นักพัฒนาซอฟต์แวร์ไม่จำเป็นต้องเข้าใจรายละเอียดเล็กๆ น้อยๆ ของลักษณะการทำงานของเบราว์เซอร์ ใน Chrome 56 เราจะกำหนดค่าเริ่มต้นให้ แตะผู้ฟังให้เป็นแบบพาสซีฟโดยค่าเริ่มต้นในกรณีที่บ่อยที่สุดตรงกับความตั้งใจของนักพัฒนาซอฟต์แวร์ เราเชื่อว่าการดำเนินการนี้จะช่วยปรับปรุงประสบการณ์ของผู้ใช้ได้อย่างมากขณะที่ส่งผลกระทบในเชิงลบต่อเว็บไซต์น้อยที่สุด

ในบางกรณีที่เกิดขึ้นไม่บ่อยนัก การเปลี่ยนแปลงนี้อาจส่งผลให้การเลื่อนโดยไม่ตั้งใจ ปัญหานี้มักจะแก้ได้ง่ายๆ โดยใช้รูปแบบ touch-action: none กับองค์ประกอบที่ไม่ควรเลื่อน อ่านต่อเพื่อดูรายละเอียด วิธีดูว่าคุณได้รับผลกระทบหรือไม่ และทำอะไรได้บ้าง

ข้อมูลภูมิหลัง: กิจกรรมที่ยกเลิกได้จะทำให้หน้าเว็บช้าลง

หากเรียกใช้ preventDefault() ในเหตุการณ์ touchstart หรือ touchmove รายการแรก จะทำให้ไม่สามารถเลื่อนได้ ปัญหาคือผู้ฟังส่วนใหญ่จะไม่เรียกใช้ preventDefault() แต่เบราว์เซอร์จะต้องรอให้เหตุการณ์เสร็จสิ้นก่อนจึงจะตรวจสอบได้ "Listener เหตุการณ์แบบแพสซีฟ" ที่นักพัฒนาแอปกำหนดช่วยแก้ปัญหานี้ได้ เมื่อคุณเพิ่มเหตุการณ์การแตะที่มีออบเจ็กต์ {passive: true} เป็นพารามิเตอร์ที่ 3 ในแฮนเดิลเหตุการณ์ หมายความว่าคุณกำลังบอกเบราว์เซอร์ว่า Listener touchstart จะไม่เรียกใช้ preventDefault() และเบราว์เซอร์จะเลื่อนได้อย่างปลอดภัยโดยไม่บล็อก Listener เช่น

window.addEventListener("touchstart", func, {passive: true} );

การฝึกฝน

แรงจูงใจหลักของเราคือการลดเวลาที่ใช้ในการอัปเดตจอแสดงผลหลังจากผู้ใช้แตะหน้าจอ เพื่อทำความเข้าใจการใช้งาน Touchstart และ Touchmove เราได้เพิ่มเมตริกเพื่อระบุความถี่ของการบล็อกการเลื่อนที่เกิดขึ้น

เราดูเปอร์เซ็นต์ของกิจกรรมการแตะที่ยกเลิกได้ซึ่งส่งไปยังเป้าหมายรูท (หน้าต่าง เอกสาร หรือเนื้อหา) และพิจารณาแล้วว่า 80% ของผู้ฟังเหล่านี้มีแนวคิดเป็นแบบแพสซีฟ แต่ไม่ได้ลงทะเบียนไว้ จากจำนวนปัญหานี้ เราสังเกตเห็นโอกาสอันยอดเยี่ยมในการปรับปรุงการเลื่อน โดยที่นักพัฒนาซอฟต์แวร์ไม่ต้องดำเนินการใดๆ ด้วยการทำให้เหตุการณ์เหล่านี้เป็นแบบ "แพสซีฟ" โดยอัตโนมัติ

จึงได้กําหนดวิธีการแทรกแซงของเราว่า หากเป้าหมายของ Listener แบบทัชอัปหรือย้ายคือ 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% ในอนาคต เราหวังว่าจะทำให้การไม่ใช้งานจริงแบบแพสซีฟเป็นค่าเริ่มต้นสำหรับ Listener ทั้งหมด touchstart และ touchmove โดยลดค่านี้ให้ต่ำกว่า 50 มิลลิวินาที

กราฟเวลา Sroll สูงสุด 1%

ความเสียหายและคำแนะนำ

ในกรณีส่วนใหญ่ การเกิดความเสียหายจะไม่เกิดขึ้น แต่เมื่อเกิดความเสียหายขึ้น อาการที่พบบ่อยที่สุดคือการเลื่อนจะเกิดขึ้นเมื่อคุณไม่ต้องการ ในบางกรณีที่เกิดขึ้นไม่บ่อยนัก นักพัฒนาซอฟต์แวร์อาจสังเกตเห็นเหตุการณ์คลิกที่ไม่คาดคิด (เมื่อ preventDefault() หายไปจาก Listener touchend)

ใน Chrome 56 ขึ้นไป เครื่องมือสำหรับนักพัฒนาเว็บจะบันทึกคำเตือนเมื่อคุณเรียกใช้ 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 กับภาพสไลด์ดังกล่าว เพื่อให้ผู้ใช้ยังคงเลื่อนในแนวตั้งและซูมได้ตามปกติ การใช้การแตะอย่างถูกต้องเป็นสิ่งจำเป็นอยู่แล้วในเบราว์เซอร์ เช่น Edge ของเดสก์ท็อปที่รองรับเหตุการณ์ของตัวชี้ ไม่ใช่ "กิจกรรมการแตะ" สำหรับ Safari บนอุปกรณ์เคลื่อนที่และเบราว์เซอร์ในอุปกรณ์เคลื่อนที่รุ่นเก่าที่ไม่รองรับการแตะ Listener จะต้องเรียกใช้ preventDefault ต่อไปแม้ว่า Chrome จะไม่สนใจการทำงานดังกล่าวก็ตาม

ในกรณีที่มีความซับซ้อน อาจจำเป็นต้องใช้การดำเนินการอย่างใดอย่างหนึ่งต่อไปนี้ด้วย

  • หาก Listener ของ touchstart เรียกใช้ preventDefault() ให้ตรวจสอบว่ามีการเรียกpreventDefault() จาก Listener ปลายทางที่เกี่ยวข้องด้วยเพื่อระงับการสร้างเหตุการณ์การคลิกและลักษณะการแตะที่เป็นค่าเริ่มต้นอื่นๆ ต่อไป
  • สุดท้าย (และไม่แนะนำให้ใช้) ให้ส่งผ่าน {passive: false} ไปยัง addEventListener() เพื่อลบล้างลักษณะการทำงานเริ่มต้น โปรดทราบว่าคุณจะต้องใช้ฟีเจอร์เพื่อตรวจจับว่า User Agent รองรับ EventListenerOptions หรือไม่

บทสรุป

ใน Chrome 56 การเลื่อนเริ่มทํางานได้เร็วขึ้นมากในหลายเว็บไซต์ นี่เป็นผลกระทบเดียวที่นักพัฒนาแอปส่วนใหญ่จะเห็นจากการเปลี่ยนแปลงนี้ ในบางกรณี นักพัฒนาซอฟต์แวร์อาจสังเกตเห็นการเลื่อนโดยไม่ตั้งใจ

แม้ว่าการดำเนินการดังกล่าวจะยังจำเป็นต่อการใช้งานสำหรับ Safari บนอุปกรณ์เคลื่อนที่ แต่เว็บไซต์ไม่ควรอาศัยการเรียกใช้ preventDefault() ภายในผู้ฟัง touchstart และผู้ฟัง touchmove เนื่องจากเราไม่รับประกันว่าจะยึดตามฟังก์ชันดังกล่าวใน Chrome อีกต่อไป นักพัฒนาซอฟต์แวร์ควรใช้พร็อพเพอร์ตี้ CSS touch-action กับองค์ประกอบที่ควรปิดใช้การเลื่อนและการซูมเพื่อแจ้งเตือนเบราว์เซอร์ก่อนที่จะเกิดเหตุการณ์การแตะขึ้น หากต้องการระงับลักษณะการทำงานเริ่มต้นของการแตะ (เช่น การสร้างเหตุการณ์การคลิก) ให้เรียกใช้ preventDefault() ใน Listener touchend