ชี้ไปข้างหน้า

Sérgio Gomes

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

อย่างไรก็ตาม ความเรียบง่ายไม่ได้หมายความว่าจะดีเสมอไป เมื่อเวลาผ่านไป พฤติกรรมของผู้ใช้ สิ่งที่สำคัญคือ ทุกสิ่งไม่ได้เป็น (หรือปลอมเป็น) หนู คุณสามารถมี ปากกาที่ไวต่อแรงกดและรับรู้การเอียง เพื่ออิสระในการสร้างสรรค์อันน่าทึ่ง คุณ ใช้นิ้วของคุณ ดังนั้น แค่มีอุปกรณ์และมือเท่านั้น แล้วทำไม ไม่ใช้นิ้วเกิน 1 นิ้วขณะวางอยู่หรือเปล่า

เรามีเหตุการณ์การแตะ มาระยะหนึ่งแล้วเพื่อช่วยเราในเรื่องนี้ แต่เป็น API ที่แยกต่างหากอย่างสิ้นเชิง สำหรับการสัมผัสโดยเฉพาะ โดยจะบังคับให้คุณต้องเขียนโค้ดรูปแบบเหตุการณ์ 2 รูปแบบแยกกัน ต้องการรองรับทั้งเมาส์และการแตะ Chrome 55 มาพร้อมกับมาตรฐานที่ใหม่กว่า ซึ่งรวมโมเดลทั้ง 2 แบบเข้าด้วยกัน คือ เหตุการณ์ตัวชี้

รูปแบบเหตุการณ์เดียว

กิจกรรม Pointer จะรวม รูปแบบอินพุตตัวชี้สำหรับเบราว์เซอร์ ซึ่งนำการแตะ ปากกา และเมาส์มารวมกัน ให้เป็นชุดเหตุการณ์เดียว เช่น

document.addEventListener('pointermove',
    ev => console.log('The pointer moved.'));
foo.addEventListener('pointerover',
    ev => console.log('The pointer is now over foo.'));

นี่คือรายการกิจกรรมทั้งหมดที่มีอยู่ ซึ่งน่าจะดูค่อนข้างคุ้นเคยหาก ที่คุณคุ้นเคยกับเหตุการณ์ด้วยเมาส์:

pointerover ตัวชี้เข้ามาอยู่ในช่องขอบเขตขององค์ประกอบ การดำเนินการนี้จะเกิดขึ้นทันทีสำหรับอุปกรณ์ที่รองรับการวางเคอร์เซอร์ หรือก่อน pointerdown เหตุการณ์สำหรับอุปกรณ์ที่ไม่มีเหตุการณ์ดังกล่าว
pointerenter คล้ายกับ pointerover แต่ไม่แสดงเป็นบับเบิลและแฮนเดิล องค์ประกอบสืบทอดจะต่างกัน รายละเอียดในข้อกำหนด
pointerdown ตัวชี้ได้เข้าสู่สถานะปุ่มเปิดใช้งาน โดยที่ปุ่มหนึ่งจะเป็น มีการกดหรือสัมผัสขึ้น ขึ้นอยู่กับความหมายของแท็ก อุปกรณ์อินพุต
pointermove เคอร์เซอร์เปลี่ยนตำแหน่งแล้ว
pointerup เคอร์เซอร์ออกจากสถานะปุ่มที่ใช้งานอยู่แล้ว
pointercancel มีบางอย่างเกิดขึ้นซึ่งหมายความว่าตัวชี้นี้จะไม่ปล่อยสัญญาณ กิจกรรมอื่นๆ ซึ่งหมายความว่าคุณควรยกเลิกการดำเนินการที่อยู่ระหว่างดำเนินการ และ กลับสู่สถานะอินพุต ที่เป็นกลาง
pointerout เคอร์เซอร์ออกจากกรอบล้อมรอบขององค์ประกอบหรือหน้าจอ และหลังจาก pointerup หากอุปกรณ์ไม่รองรับการวางเมาส์เหนือ
pointerleave คล้ายกับ pointerout แต่ไม่แสดงเป็นบับเบิลและแฮนเดิล องค์ประกอบสืบทอดจะต่างกัน รายละเอียดในข้อกำหนด
gotpointercapture องค์ประกอบได้รับการจับภาพตัวชี้
lostpointercapture เคอร์เซอร์ที่กำลังบันทึกไว้ได้รับการบันทึกแล้ว เผยแพร่แล้ว

อินพุตประเภทต่างๆ

โดยทั่วไปแล้ว เหตุการณ์ตัวชี้จะ ให้คุณเขียนโค้ดได้แบบไม่ต้องพึ่งพิง โดยไม่ต้องลงทะเบียนเครื่องจัดการเหตุการณ์แยกต่างหากสำหรับอุปกรณ์อินพุตที่ต่างกัน แน่นอนว่าคุณยังคงต้องคำนึงถึงความแตกต่างระหว่างอินพุตประเภทต่างๆ เช่น การใช้การวางเมาส์ หากต้องการแยกแยะอุปกรณ์อินพุตประเภทต่างๆ ออกจากกัน อาจเป็น แยกโค้ด/ฟังก์ชันสำหรับอินพุตต่างๆ - แต่คุณสามารถทำเช่นนั้นได้จาก ภายในเครื่องจัดการเหตุการณ์เดียวกันโดยใช้พร็อพเพอร์ตี้ pointerType ของ PointerEvent ของ Google เช่น ถ้ากำลังเขียนโค้ดลิ้นชักการนำทางด้านข้าง ใช้ตรรกะต่อไปนี้ในเหตุการณ์ pointermove

switch(ev.pointerType) {
    case 'mouse':
    // Do nothing.
    break;
    case 'touch':
    // Allow drag gesture.
    break;
    case 'pen':
    // Also allow drag gesture.
    break;
    default:
    // Getting an empty string means the browser doesn't know
    // what device type it is. Let's assume mouse and do nothing.
    break;
}

การทำงานเริ่มต้น

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

เมื่อใช้เหตุการณ์ตัวชี้ เมื่อใดก็ตามที่มีการเรียกการกระทำเริ่มต้น เช่น การเลื่อนหรือการซูม คุณจะเห็นเหตุการณ์ pointercancel เพื่อแจ้งให้ทราบว่าเบราว์เซอร์ได้บันทึก การควบคุมเคอร์เซอร์ เช่น

document.addEventListener('pointercancel',
    ev => console.log('Go home, the browser is in charge now.'));

ความเร็วในตัว: โมเดลนี้ช่วยให้ประสิทธิภาพดีขึ้นโดยค่าเริ่มต้น เมื่อเทียบกับกิจกรรมการสัมผัส ซึ่งคุณจะต้องใช้ Listener เหตุการณ์แบบแพสซีฟ เพื่อให้ได้การตอบสนองในระดับเดียวกัน

คุณสามารถหยุดไม่ให้เบราว์เซอร์ควบคุมด้วย touch-action พร็อพเพอร์ตี้ CSS การตั้งค่าเป็น none ในองค์ประกอบจะเป็นการปิดใช้ทั้งหมด การทำงานที่กำหนดโดยเบราว์เซอร์เริ่มต้นขึ้นในองค์ประกอบนั้น แต่ก็มีคำตอบ ค่าอื่นๆ สำหรับการควบคุมโดยละเอียดยิ่งขึ้น เช่น pan-x สำหรับการอนุญาต เบราว์เซอร์ให้ตอบสนองต่อการเคลื่อนที่บนแกน x แต่ไม่ใช่แกน y Chrome 55 รองรับค่าต่อไปนี้

auto ค่าเริ่มต้น; เบราว์เซอร์สามารถดำเนินการตามค่าเริ่มต้นได้
none เบราว์เซอร์ไม่ได้รับอนุญาตให้ดำเนินการเริ่มต้น
pan-x เบราว์เซอร์ได้รับอนุญาตให้ดำเนินการตามค่าเริ่มต้นของการเลื่อนแนวนอนเท่านั้น
pan-y เบราว์เซอร์ได้รับอนุญาตให้ดำเนินการตามค่าเริ่มต้นของการเลื่อนแนวตั้งเท่านั้น
pan-left เบราว์เซอร์ได้รับอนุญาตให้ดำเนินการตามค่าเริ่มต้นของการเลื่อนแนวนอนเท่านั้น และเลื่อนหน้าไปทางซ้ายเท่านั้น
pan-right เบราว์เซอร์ได้รับอนุญาตให้ดำเนินการตามค่าเริ่มต้นของการเลื่อนแนวนอนเท่านั้น และเลื่อนหน้าไปทางขวาเท่านั้น
pan-up เบราว์เซอร์ได้รับอนุญาตให้ดำเนินการตามค่าเริ่มต้นของการเลื่อนแนวตั้งเท่านั้น และเลื่อนหน้าขึ้นเท่านั้น
pan-down เบราว์เซอร์ได้รับอนุญาตให้ดำเนินการตามค่าเริ่มต้นของการเลื่อนแนวตั้งเท่านั้น และเลื่อนหน้าลงเท่านั้น
manipulation เบราว์เซอร์ได้รับอนุญาตให้ดำเนินการเลื่อนและซูมเท่านั้น

การจับภาพเคอร์เซอร์

เคยใช้เวลาหนึ่งชั่วโมงที่น่าหงุดหงิดในการแก้ไขข้อบกพร่องของ mouseup ที่เสียหาย จนกว่าคุณจะรู้ว่านั่นเป็นเพราะผู้ใช้ปล่อยปุ่ม นอกเป้าหมายคลิกหรือไม่ หากไม่ โอเค งั้นก็ฉันเองนะ

แต่จนถึงตอนนี้ ก็ยังยังไม่มีวิธีที่ดีในการจัดการกับปัญหานี้ ได้เลย คุณสามารถตั้งค่าเครื่องจัดการ mouseup ในเอกสารและบันทึกสถานะ แอปพลิเคชันของคุณเพื่อติดตามสิ่งต่างๆ นั่นไม่ใช่โซลูชันที่ดูสะอาดตาที่สุด โดยเฉพาะอย่างยิ่งหากคุณกำลังสร้างคอมโพเนนต์เว็บและพยายามทำให้ทุกอย่างสวยงาม เป็นแห่งๆ

เหตุการณ์ของตัวชี้เป็นโซลูชันที่ดีกว่ามาก นั่นคือคุณสามารถจับภาพตัวชี้ เพื่อให้คุณได้รับกิจกรรม pointerup นั้น (หรือกิจกรรมอื่นๆ ที่เข้าใจยาก เพื่อน)

const foo = document.querySelector('#foo');
foo.addEventListener('pointerdown', ev => {
    console.log('Button down, capturing!');
    // Every pointer has an ID, which you can read from the event.
    foo.setPointerCapture(ev.pointerId);
});

foo.addEventListener('pointerup', 
    ev => console.log('Button up. Every time!'));

การสนับสนุนเบราว์เซอร์

ในขณะที่เขียน ระบบรองรับเหตุการณ์ตัวชี้ใน Internet Explorer 11 Microsoft Edge, Chrome และ Opera รองรับบางส่วนใน Firefox คุณสามารถ ดูรายการล่าสุดใน caniuse.com

คุณสามารถใช้ polyfill เหตุการณ์ของ Pointer สำหรับ เติมเต็มช่องว่าง อีกทางเลือกหนึ่งคือ การตรวจหาการรองรับเบราว์เซอร์ขณะรันไทม์คือ ตรงไปตรงมา:

if (window.PointerEvent) {
    // Yay, we can use pointer events!
} else {
    // Back to mouse and touch events, I guess.
}

เหตุการณ์ตัวชี้เป็นตัวเลือกที่ยอดเยี่ยมสำหรับการปรับปรุงแบบต่อเนื่อง เพียงแค่ แก้ไขวิธีการเริ่มต้นเพื่อตรวจสอบข้างต้น เพิ่มเหตุการณ์ตัวชี้ ในบล็อก if และเลื่อนเครื่องจัดการเหตุการณ์เมาส์/การแตะไปที่ บล็อก else

เอาล่ะ เราอยากให้พวกเขาลองใช้และบอกเราว่าคุณคิดอย่างไร