อินพุตช่วงคู่แบบเนทีฟ
(muffinman.io)@stanko/dual-range-inputสร้างสไลเดอร์ช่วงคู่ด้วยการนำ เนทีฟ HTML range input สองตัวมารวมกัน โดยคงการโต้ตอบและการเข้าถึงพื้นฐานของเบราว์เซอร์ไว้ตามเดิม- การทำงานหลักอาศัย JavaScript ราว 50 บรรทัด สำหรับซิงก์ค่าของอินพุตและ CSS จึงเรียบง่ายกว่าวิธีเดิมที่ต้องสร้างการลากและการเข้าถึงขึ้นมาใหม่เอง
- อินพุตทั้งสองจะอัปเดต
min,maxและความกว้างให้สอดคล้องกับ จุดกึ่งกลาง ของค่าที่เลือก และเมื่อคลิกบนแทร็ก thumb ที่อยู่ใกล้กว่าจะย้ายไปยังค่านั้น - เนื่องจากแทร็กของ range input สั้นกว่าความกว้างจริงของอินพุต จึงต้องบวกความกว้างของ thumb เข้าไปในการคำนวณ และใช้ padding ของ wrapper เพื่อรองรับความกว้างส่วนเกิน
- ธีมสามารถปรับได้ด้วยตัวแปร CSS และ gradient จึงสามารถ override สีของช่วงที่เลือก ขนาด thumb สีแทร็ก และส่วนอื่น ๆ ได้
สไลเดอร์คู่ที่สร้างด้วยเนทีฟ range input
@stanko/dual-range-inputใช้ HTML range input สองตัววางเคียงกันเพื่อทำอินพุตช่วงคู่- คำว่า “เนทีฟ” ในที่นี้ไม่ได้หมายถึงคอนโทรลของเบราว์เซอร์เพียงตัวเดียว แต่หมายถึงการใช้ range input สองตัวเพื่อคงการโต้ตอบพื้นฐานของเบราว์เซอร์และ ความสามารถด้านการเข้าถึง ไว้
- การติดตั้งประกอบด้วย JavaScript ราว 50 บรรทัด สำหรับซิงก์อินพุต และ CSS สำหรับความสมบูรณ์ด้านภาพ
- จุดเริ่มต้นมาจากความต้องการ UI สำหรับปรับค่าต่ำสุดและค่าสูงสุดของพารามิเตอร์ในเครื่องมือวาดภาพเชิงกำเนิด
- มีข้อกำหนดอยู่สองข้อ
- ต้องใช้ HTML range input แบบเนทีฟ
- เมื่อคลิกที่แทร็ก thumb ที่อยู่ใกล้จุดคลิกมากกว่าจากสองตัวต้องย้ายไปยังค่านั้น
การซิงก์ค่าและการจัดการการคลิก
- เมื่ออินพุตตัวใดตัวหนึ่งเปลี่ยนค่า ไลบรารีจะคำนวณ จุดกึ่งกลาง ระหว่างค่าที่เลือกทั้งสอง
- ตั้งค่า
minและmaxของแต่ละอินพุตให้ตรงกับจุดกึ่งกลาง - อัปเดตความกว้างของอินพุตทั้งสองโดยอิงจากจุดกึ่งกลางด้วย
- ตั้งค่า
- เนื่องจากมีการปรับขนาดให้อินพุตมาบรรจบกันที่จุดกึ่งกลาง เมื่อคลิกระหว่าง thumb ทั้งสอง thumb ที่อยู่ใกล้ตำแหน่งคลิกกว่าจะเคลื่อนที่
- ถ้าจำนวน step ระหว่าง thumb ทั้งสองเป็นเลขคี่ จะให้อินพุตที่ใช้งานล่าสุดมีลำดับความสำคัญ
การคำนวณ CSS และการตกแต่งสไตล์
- แทร็กของ range input สั้นกว่าความกว้างเต็มของอินพุต และเบราว์เซอร์จะเว้นพื้นที่ด้านซ้ายขวาไว้เพื่อไม่ให้ thumb ล้นออกไปด้านนอก
- ถ้าแบ่งความกว้างอินพุตแบบง่าย ๆ เช่น 25% และ 75% สัดส่วนของแทร็กจริงจะไม่ตรง
- การทำงานนี้จึงบวก
--dri-thumb-widthเข้าไปในความกว้างของแต่ละอินพุต และเพิ่ม padding ให้ wrapper เพื่อรองรับความกว้างส่วนเกิน
- การตกแต่งสไตล์ใช้ความสามารถในการจัดสไตล์ CSS ของ range input ที่เบราว์เซอร์มีให้
- มีการตกแต่ง track และ thumb และจัดการไม่ให้เกิด border radius ที่ส่วนกึ่งกลางซึ่งแทร็กทั้งสองมาชนกัน
- ธีมสามารถสร้างได้ด้วยการ override ตัวแปร CSS
- ตัวแปรหลักได้แก่
--dri-height,--dri-thumb-width,--dri-thumb-color,--dri-track-color,--dri-track-filled-color,--dri-track-border-radiusเป็นต้น
- ตัวแปรหลักได้แก่
- ช่วงที่เลือกจะถูกระบายด้วย CSS gradient
- ตำแหน่งของ gradient ระบุด้วยตัวแปร
--dri-gradient-position - ในโค้ดจะอัปเดตตัวแปรนี้พร้อมกันเมื่อมีการอัปเดตความกว้างของอินพุต
- ตำแหน่งของ gradient ระบุด้วยตัวแปร
- แนวทางนี้เลือกใช้ องค์ประกอบแบบเนทีฟ ให้มากที่สุด แทนการสร้างการลากและการเข้าถึงขึ้นใหม่ด้วยไลบรารีแบบคัสตอม
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
คิดว่าสไลเดอร์แบบนี้ ควรลากพื้นที่ตรงกลางได้เสมอ ด้วย กล่าวคือถ้าจับตรงกลาง ก็ควรให้ตัวจับทั้งสองขยับไปพร้อมกัน
อ้างอิงว่า Unity จัดการแบบนี้: https://docs.unity3d.com/StaticFiles/ScriptRefImages/EditorG...
คำว่า “เสมอ” อาจแรงไปหน่อย
ผู้เขียนเองครับ ดีใจที่ขึ้น HN
เห็นว่าคนเริ่มทิ้ง issue กับ bug report ไว้ที่นี่แล้ว ที่นี่ก็ดึกมากแล้ว เลยกะว่าจะไปดูพรุ่งนี้
ผมทำมันขึ้นมาเพราะต้องใช้กับงานสร้างภาพเชิงกำเนิด และผมชอบลากสไลเดอร์ดูว่าภาพเปลี่ยนยังไงมากกว่าพิมพ์ตัวเลข
พยายามคงความ มินิมอล ไว้ให้มากที่สุด
ตอนนี้ผมใช้ SwiftUI ซึ่งมันง่ายมากจนแทบง่ายเกินไปที่จะสร้างคอมโพเนนต์เอง แต่ตามที่ผู้เขียนอธิบายไว้ มันไม่ได้เป็นทางเลือกที่ดีที่สุดเสมอไป และคอมโพเนนต์เนทีฟก็มักมีฟังก์ชันค่อนข้างครบ
ผมลองเปิดบทความนี้ใน Firefox บน iOS ซึ่งเป็นเบราว์เซอร์ที่ค่อนข้างแปลกอยู่เหมือนกัน แล้วมันก็ทำงานได้ดีมาก สร้างมาดีจริงๆ
ลองใช้เองแล้วเจอบั๊กทันที ถ้าลากสไลเดอร์ไปที่ 100 - 100 แล้วจะทำให้เป็น 99 - 99 มันทำไม่ได้
จาก 99 - 100 ก็ไปไม่ได้ และจาก 98 - 99 ก็ไปไม่ได้เช่นกัน วิธีเดียวที่จะไป 99 - 99 ได้คือต้องลากค่าต่ำสุดลงไปให้ต่ำกว่า 98 ก่อน
พออยู่ที่ 98 - 99 แล้วลดลงเป็น 97 - 99 ตอนนั้นถึงจะไป 99 - 99 ได้
บั๊กนี้เกิดได้ทุกที่ เพียงแต่ 99 - 99 อยู่ใกล้ปลายสไลเดอร์เลยทำให้ทดสอบซ้ำได้ง่ายที่สุด โดยทั่วไป เมื่ออยู่ในสถานะ X - X ถ้าขยับปลายด้านหนึ่งไป 1 ขั้น จะลากอีกด้านเข้ามาปิดช่องว่างนั้นไม่ได้ ต้องลากอีกด้านออกไปไกลกว่าเดิมก่อนถึงจะปิดระยะได้
ขอบคุณที่รายงาน เดี๋ยวผมแก้แน่นอน
แต่มีบั๊กด้านภาพอีกอย่าง เริ่มจาก 23-25 แล้วลากเป็น 24-25 ตัวจับด้านขวาที่อยู่ที่ 25 จะขยับไปไม่กี่พิกเซล ถ้าลากตัวจับซ้ายกลับไป 23 ตัวจับขวาก็จะกลับไปด้วย
เจ๋งมาก! ผมคิดมาตลอดว่า HTML input ควรมี ตัวเลือกช่วงสองค่า เพราะมันสมเหตุสมผลมาก
เช่นกรณีใช้งานอย่าง “อยากกรองสินค้าให้เหลือเฉพาะที่มีวิดเจ็ตอย่างน้อย X ชิ้น และไม่เกิน Y ชิ้น”
แต่ผมพบบั๊กที่อยากเน้นใน Firefox ทุกครั้งที่คลิกตัวจับต่ำสุดหรือสูงสุด ค่าจะเปลี่ยนขึ้นหรือลงหนึ่งขั้น
ถ้าพยายามคลิกตรงกลางของตัวจับพอดี ค่าจะไม่เปลี่ยน แต่ถ้าคลิกเยื้องไปทางซ้ายนิดหน่อย ค่าที่เลือกจะลดลงหนึ่งขั้น และถ้าคลิกเยื้องไปทางขวานิดหน่อยก็จะเพิ่มขึ้น
พฤติกรรมที่คาดหวังคือค่าควรเปลี่ยนก็ต่อเมื่อมี การกระทำเปลี่ยนค่าอย่างชัดเจน คือกดปุ่มค้างแล้วเลื่อนตัวชี้เมาส์จริงๆ เท่านั้น
Firefox ตีความการคลิกแบบนั้นเหมือนเป็นการคลิกบนแทร็กโดยตรง ไลบรารีนี้ไม่ได้ไปแทรกแซงอะไรตรงนั้นเลย มันเป็นแค่ พฤติกรรมของ Firefox
ยอดเยี่ยมที่มัน ใช้งานด้วยคีย์บอร์ดได้ และทำงานถูกต้องด้วย ถึงขั้นกด Shift-Tab ค้างไว้เพื่อย้อนกลับได้
ทำออกมาได้ดีมาก และถึงจะเป็นรายละเอียดเล็กมาก แต่ก็ขอบคุณที่ทำอินเทอร์แอ็กชันนี้ได้ถูกต้อง
ใน ดีไซน์ซิสเต็ม ของบริษัทเรา เด็กฝึกงานคนหนึ่งเคยทำ proof of concept แบบวางเคียงกันลักษณะนี้เหมือนกัน แต่เจอปัญหาพิกเซลของ input ตัวหนึ่งกระโดดไปมาทีละนิดตลอด สุดท้ายเลยทำให้ดีจริงๆ ไม่ได้
สุดท้ายเด็กฝึกงานอีกคนเลยเสนอทางแก้ด้วยการใช้ input range 2 ตัวซ้อนกัน
ทุกครั้งที่ต้องใช้ range input ผมใช้ jQuery UI slider และต้องใส่ทั้ง jQuery กับ jQuery UI ลงในหน้าเพราะสไลเดอร์ตัวเดียว อันนี้ดีมากจริงๆ
แก้ไข: มีโอกาสจะเตรียม ไฟล์ JS ปกติ ที่ใช้ได้เลยแทน ESM module ไหม?
"export default"ท้ายไฟล์ออกก็ใช้ได้https://cdn.jsdelivr.net/npm/@stanko/dual-range-input@0.9.8/...
อย่าลืม CSS ด้วย
https://cdn.jsdelivr.net/npm/@stanko/dual-range-input@0.9.8/...
ไม่แน่ใจว่าควรรายงานบั๊กที่นี่ไหม ตัวจับของสองตัวอย่างแรก ดักจับการโต้ตอบแบบสัมผัสจนทำให้เลื่อนหน้าไม่ได้ แต่สามตัวอย่างหลังไม่เป็น
ผลข้างเคียงคือไม่สามารถลากขึ้นเพื่อให้เห็นป้ายใต้ปลายนิ้วได้ และหน้าก็จะเลื่อน ทำให้ป้ายยังอยู่ใต้ปลายนิ้วต่อไป
อยากทราบว่าคุณใช้เบราว์เซอร์กับระบบปฏิบัติการอะไร ไลบรารีนี้ใช้ native input จึงไม่ได้ทำอะไรเกี่ยวกับการบล็อกการเลื่อนเลย
บนโทรศัพท์ iOS ของผม ไม่ว่าจะใส่สไตล์หรือไม่
input[type=range]ทุกตัวก็บล็อกการเลื่อนเหมือนกัน[1] https://github.com/Stanko/dual-range-input/issues
คำว่า “เนทีฟ” ยังถกเถียงกันได้อยู่ แม้ไลบรารีนี้จะใช้ HTML range input เนทีฟสองตัว จึงยังคงพฤติกรรมแบบเนทีฟและความสามารถด้าน accessibility เอาไว้ แต่สำหรับผมเกณฑ์ชัดเจนมาก
ถ้าจะให้มันทำงานตามที่ตั้งใจไว้แล้ว ต้องใช้ JavaScript มันก็ไม่ใช่เนทีฟอีกต่อไป
แต่ก็เข้าใจว่าผู้เขียนตั้งใจจะสื่ออะไร เพราะแทนที่จะทำก้อน div ที่ขยับด้วย JS เอง เขาเอาสไลเดอร์ปกติมาประกอบกัน เลยใกล้กับวิธีแก้แบบเนทีฟมากกว่า ถ้าแทน JavaScript ด้วย CSS เจ๋งๆ ไม่ได้ นี่ก็น่าจะเป็นวิธีที่ใกล้ที่สุดเท่าที่จะทำได้
ไม่ว่าจะเรียกเนทีฟหรือไม่ วิดเจ็ตแบบนี้ก็มีประโยชน์มากถ้าใช้งานได้ และในพื้นที่นี้ก็ยังมี implementation อยู่ไม่มาก
ก่อนจะมี
popoverattribute มันเปิดไม่ได้เลยถ้าไม่มี JavaScriptและยังมี เมธอด native vanilla JavaScript ที่พอจะแยกให้ต่างจากไลบรารีที่ไม่ใช้เครื่องมือตรวจสอบความถูกต้องของฟอร์มจากเบราว์เซอร์ภายในเลยได้เช่นกัน
ตอนทำ https://spodj.intermer.net/ ผมกำลังมองหาของแบบนี้อยู่
สุดท้ายต้องใช้ React แบบงูๆ ปลาๆ เพื่อให้ได้ฟังก์ชันที่ต้องการ เพราะผมไม่ค่อยเก่งงานฟรอนต์เอนด์นัก