เจาะลึกเบื้องหลัง: การลาก (Drag), การสัมผัส (Touch) และ CSS Cascade
การสร้าง UI panel ที่ปรับขนาดได้นั้นยากกว่าที่เห็น คุณต้องจัดการทั้งเลย์เอาต์ (layouts), เหตุการณ์การสัมผัส (touch events) และกฎ CSS ไปพร้อมๆ กัน
และนี่คือ 3 บทเรียนทางเทคนิคจากการปรับปรุงดีไซน์ (redesign) ของ visual-diff UI ของผมเมื่อเร็วๆ นี้
- เลิกใช้ความกว้างของหน้าต่าง (window width) สำหรับการจัดเลย์เอาต์
อย่าใช้ window.innerWidth เพื่อตัดสินว่าเลย์เอาต์ของคุณเป็นแนวตั้งหรือแนวนอน เพราะหากผู้ใช้ปรับขนาดหน้าต่าง ตรรกะ (logic) ของคุณจะผิดพลาดทันที
ให้ใช้สถานะ CSS จริงแทน โดยใช้ getComputedStyle เพื่อตรวจสอบ flex-direction วิธีนี้จะอ่านค่าจริงหลังจากที่ media queries ทั้งหมดถูกนำมาใช้แล้ว ซึ่งจะช่วยให้มั่นใจได้ว่าตรรกะการลากของคุณตรงกับสิ่งที่ผู้ใช้เห็นจริงๆ
- จัดการเหตุการณ์ touch และ mouse ไปพร้อมกัน
เพื่อให้การลากใช้งานได้ทั้งบนเดสก์ท็อปและมือถือ คุณจำเป็นต้องใช้วิธีการที่เป็นหนึ่งเดียวกัน
- ใช้ optional chaining เพื่อดึงค่าพิกัด (coordinates) วิธีนี้จะช่วยให้ฟังก์ชันเดียวสามารถจัดการได้ทั้งเหตุการณ์ mouse และ touch
- ใช้
{ passive: false }สำหรับtouchmoveโดยปกติเบราว์เซอร์จะตั้งค่า touch events เป็น passive เพื่อช่วยในการเลื่อนหน้าจอ (scrolling) หากคุณไม่ตั้งค่านี้เป็น false คุณจะไม่สามารถเรียกใช้e.preventDefault()ได้ และหากไม่มีการเรียกใช้นั้น หน้าเว็บจะเลื่อนไปมาในขณะที่คุณกำลังพยายามลาก - เชื่อมต่อ (attach) ตัวฟังเหตุการณ์ (listeners)
moveและendเข้ากับdocumentหากผู้ใช้ขยับนิ้วเร็วเกินไป พวกเขาอาจจะหลุดออกจากตัวแบ่ง (divider) การเชื่อมต่อกับdocumentจะช่วยให้การลากดำเนินต่อไปได้แม้ว่าตัวชี้ (pointer) จะเคลื่อนที่ออกไปก็ตาม
- หลีกเลี่ยงการขัดแย้งกับระบบการแสดงผล (visibility system)
ผมเคยเจอบั๊กเรื่อง CSS specificity โดยผมมีคลาสหนึ่งสำหรับซ่อน panel และอีกคลาสหนึ่งสำหรับกำหนดทิศทางของเลย์เอาต์
ปัญหาคือ:
.view-panel { display: none; }.diff-layout { display: flex; }
เนื่องจากทั้งสองมีค่า specificity เท่ากัน คลาสที่อยู่ลำดับสุดท้ายในไฟล์จึงมีผลมากกว่า ซึ่งหมายความว่า panel ของผมยังคงแสดงผลอยู่ ทั้งที่จริงๆ แล้วมันควรจะถูกซ่อนไว้
วิธีแก้ไข:
อย่าปล่อยให้คลาสช่วยจัดเลย์เอาต์ (layout helper class) เป็นตัวกำหนดคุณสมบัติ display แต่ควรให้คลาสที่ควบคุมการแสดงผล (visibility class) เป็นผู้จัดการเรื่องนั้น
แทนที่จะใช้:
.diff-layout { display: flex; flex-direction: column; }
ให้ใช้:
.view-panel.diff-layout { flex-direction: column; }
วิธีนี้จะช่วยเพิ่มค่า specificity และทำให้มั่นใจได้ว่าคลาสเลย์เอาต์จะเปลี่ยนเฉพาะทิศทางเท่านั้น ไม่ใช่การแสดงผล
ที่มา: https://dev.to/bonzai2carn/under-the-hood-drag-touch-and-css-cascade-in-a-real-diff-ui-1b66
