เจาะลึกเบื้องหลัง: การลาก (Drag), การสัมผัส (Touch) และ CSS Cascade

การสร้าง UI panel ที่ปรับขนาดได้นั้นยากกว่าที่เห็น คุณต้องจัดการทั้งเลย์เอาต์ (layouts), เหตุการณ์การสัมผัส (touch events) และกฎ CSS ไปพร้อมๆ กัน

และนี่คือ 3 บทเรียนทางเทคนิคจากการปรับปรุงดีไซน์ (redesign) ของ visual-diff UI ของผมเมื่อเร็วๆ นี้

  1. เลิกใช้ความกว้างของหน้าต่าง (window width) สำหรับการจัดเลย์เอาต์

อย่าใช้ window.innerWidth เพื่อตัดสินว่าเลย์เอาต์ของคุณเป็นแนวตั้งหรือแนวนอน เพราะหากผู้ใช้ปรับขนาดหน้าต่าง ตรรกะ (logic) ของคุณจะผิดพลาดทันที

ให้ใช้สถานะ CSS จริงแทน โดยใช้ getComputedStyle เพื่อตรวจสอบ flex-direction วิธีนี้จะอ่านค่าจริงหลังจากที่ media queries ทั้งหมดถูกนำมาใช้แล้ว ซึ่งจะช่วยให้มั่นใจได้ว่าตรรกะการลากของคุณตรงกับสิ่งที่ผู้ใช้เห็นจริงๆ

  1. จัดการเหตุการณ์ 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) จะเคลื่อนที่ออกไปก็ตาม
  1. หลีกเลี่ยงการขัดแย้งกับระบบการแสดงผล (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