Input phạm vi kép native
(muffinman.io)@stanko/dual-range-inputkết hợp hai HTML range input native để tạo slider phạm vi kép, tận dụng nguyên vẹn tương tác mặc định và khả năng truy cập của trình duyệt- Phần triển khai chủ yếu gồm CSS và khoảng 50 dòng JavaScript để đồng bộ giá trị input, nên đơn giản hơn các cách làm truyền thống phải tự xây lại thao tác kéo và khả năng truy cập
- Hai input được cập nhật
min,maxvà chiều rộng theo điểm giữa của các giá trị đã chọn; khi nhấp vào track, thumb gần hơn sẽ di chuyển tới giá trị đó - Vì track của range input ngắn hơn chiều rộng thực tế của input, phần triển khai cộng thêm chiều rộng thumb vào phép tính và dùng padding của wrapper để hấp thụ phần chiều rộng vượt ra
- Theme có thể được điều chỉnh bằng biến CSS và gradient, cho phép override màu vùng được chọn, kích thước thumb, màu track, v.v.
Slider kép được tạo bằng range input native
@stanko/dual-range-inputtriển khai input phạm vi kép bằng cách đặt hai HTML range input cạnh nhau- Ở đây, “native” không có nghĩa là một control trình duyệt đơn lẻ, mà là dùng hai range input để giữ lại tương tác mặc định và tính năng accessibility của trình duyệt
- Phần triển khai gồm khoảng 50 dòng JavaScript để đồng bộ các input, cùng CSS để hoàn thiện phần hiển thị
- Điểm khởi đầu là nhu cầu có một UI để điều chỉnh giá trị tối thiểu và tối đa của tham số trong một công cụ vẽ tạo sinh
- Yêu cầu có hai điểm
- Sử dụng HTML range input native
- Khi nhấp vào track, thumb nào trong hai thumb gần vị trí nhấp hơn sẽ di chuyển tới giá trị đó
Đồng bộ giá trị và xử lý nhấp chuột
- Khi một trong hai input thay đổi, thư viện tính điểm giữa giữa hai giá trị được chọn
- Đặt thuộc tính
minvàmaxcủa từng input theo điểm giữa - Chiều rộng của hai input cũng được cập nhật dựa trên điểm giữa
- Đặt thuộc tính
- Vì kích thước các input được điều chỉnh để gặp nhau tại điểm giữa, khi nhấp vào khoảng giữa hai thumb, thumb gần vị trí nhấp hơn sẽ di chuyển
- Nếu số step giữa hai thumb là số lẻ, input được dùng gần nhất sẽ được ưu tiên
Tính toán CSS và styling
- Track của range input ngắn hơn toàn bộ chiều rộng input, và trình duyệt chừa khoảng trống hai bên để thumb không lòi ra ngoài
- Nếu chỉ chia chiều rộng input đơn giản như 25% và 75% thì tỷ lệ track thực tế sẽ không khớp
- Phần triển khai cộng
--dri-thumb-widthvào chiều rộng của từng input, đồng thời thêm padding cho wrapper để chứa phần chiều rộng vượt ra
- Styling sử dụng khả năng CSS styling của range input trên trình duyệt
- Trang trí track và thumb, đồng thời xử lý để phần giữa nơi hai track gặp nhau không xuất hiện border radius
- Theme có thể được tạo bằng cách override biến CSS
- Các biến chính gồm
--dri-height,--dri-thumb-width,--dri-thumb-color,--dri-track-color,--dri-track-filled-color,--dri-track-border-radius, v.v.
- Các biến chính gồm
- Phạm vi được chọn được tô bằng CSS gradient
- Vị trí gradient được chỉ định bằng biến
--dri-gradient-position - Khi cập nhật chiều rộng input trong mã, biến này cũng được cập nhật cùng lúc
- Vị trí gradient được chỉ định bằng biến
- Cách triển khai này là hướng tiếp cận ưu tiên tận dụng phần tử native nhiều nhất có thể, thay vì tự xây lại thao tác kéo và khả năng truy cập bằng thư viện tùy chỉnh
1 bình luận
Các ý kiến trên Hacker News
Tôi nghĩ loại slider này phần ở giữa cũng phải luôn kéo được. Tức là khi nắm ở giữa thì hai tay nắm ở hai bên sẽ di chuyển cùng lúc
Tham khảo: Unity xử lý như vậy: https://docs.unity3d.com/StaticFiles/ScriptRefImages/EditorG...
“Luôn luôn” có lẽ hơi quá lời
Tôi là tác giả. Rất vui khi thấy nó được đưa lên HN
Tôi thấy mọi người đã bắt đầu để lại issue và báo lỗi ở đây; chỗ tôi đã muộn nên mai tôi sẽ xem
Tôi làm cái này vì cần cho công việc tạo ảnh bằng AI, và tôi thích kéo slider để xem hình ảnh thay đổi ra sao hơn là nhập số
Tôi đã cố giữ nó tối giản nhất có thể
Hiện tôi đang dùng SwiftUI, nơi việc tự tạo component gần như quá dễ. Nhưng như tác giả giải thích, đó không phải lúc nào cũng là lựa chọn tốt nhất, và component native thường có khá đầy đủ chức năng
Tôi cũng thử xem bài trên một trình duyệt hơi đặc biệt là Firefox trên iOS, và nó hoạt động tốt. Làm rất tốt
Tôi vừa thử dùng đã gặp lỗi ngay. Sau khi kéo slider về 100 - 100, nếu muốn đưa về 99 - 99 thì không được
Từ 99 - 100 cũng không đi được, từ 98 - 99 cũng không đi được. Cách duy nhất để tới 99 - 99 là kéo cận dưới xuống thấp hơn 98
Ở trạng thái 98 - 99, nếu hạ xuống 97 - 99 thì lúc đó mới có thể tới 99 - 99
Lỗi này có thể xảy ra ở bất cứ đâu; 99 - 99 chỉ là dễ tái hiện nhất vì gần cuối slider. Nói chung, ở một trạng thái X - X nào đó, nếu di chuyển một đầu đi 1 bước, bạn không thể kéo đầu còn lại vào khoảng trống đó. Muốn đóng khoảng cách đó, trước tiên phải kéo đầu kia ra xa hơn
Cảm ơn báo cáo, tôi nhất định sẽ sửa
Nhưng có một lỗi hiển thị khác. Nếu bắt đầu ở 23-25 rồi kéo thành 24-25, tay nắm bên phải ở 25 sẽ dịch chuyển vài pixel. Khi đưa tay nắm bên trái về lại 23, tay nắm bên phải cũng quay lại
Hay đấy! Tôi luôn nghĩ HTML input cần có cách triển khai nhập khoảng với hai giá trị, vì nó khá hợp lý
Ví dụ có use case như “tôi muốn lọc các sản phẩm có số widget ít nhất là X và nhiều nhất là Y”
Tuy nhiên tôi tìm thấy một lỗi muốn nhấn mạnh trên Firefox. Mỗi lần nhấp vào tay nắm min hoặc max, giá trị lại đổi lên hoặc xuống một bước
Nếu cố nhấp đúng vào giữa tay nắm thì giá trị không đổi, nhưng nhấp hơi lệch sang trái thì giá trị được chọn giảm một bước, còn hơi lệch sang phải thì tăng
Hành vi mong đợi là giá trị chỉ đổi khi có thao tác thay đổi rõ ràng: giữ nút và thực sự di chuyển con trỏ chuột
Firefox diễn giải những cú nhấp đó như nhấp trực tiếp vào track. Thư viện này hoàn toàn không can thiệp vào đó; đó chỉ là cách Firefox hoạt động
Việc nó hoạt động bằng bàn phím là rất tuyệt. Nó hoạt động đúng, nên nếu giữ Shift-Tab thì cũng có thể di chuyển lùi
Làm thật sự tốt, và dù chỉ là chi tiết rất nhỏ, tôi vẫn biết ơn vì phần tương tác đó được triển khai chính xác
Trong design system của công ty chúng tôi, một thực tập sinh cũng từng làm proof-of-concept đặt cạnh nhau kiểu này, nhưng luôn có hiện tượng pixel của một input bị nhảy lặt vặt và cuối cùng không làm cho đúng được
Vì vậy một thực tập sinh thứ hai đã đưa ra giải pháp dùng 2 input range chồng lên nhau
Mỗi khi cần nhập khoảng, tôi đều dùng slider của jQuery UI, và phải thêm jQuery cùng jQuery UI vào trang chỉ vì một slider. Cái này thật sự tốt
Sửa: Có khả năng dựng sẵn một file JS thường dùng trực tiếp thay vì module ESM không?
"export default"ở cuối là đượchttps://cdn.jsdelivr.net/npm/@stanko/dual-range-input@0.9.8/...
Đừng quên CSS
https://cdn.jsdelivr.net/npm/@stanko/dual-range-input@0.9.8/...
Không biết báo lỗi ở đây có được không. Tay nắm trong hai ví dụ đầu chặn tương tác chạm và ngăn cuộn. Ba ví dụ sau thì không
Hệ quả là không thể kéo lên để nhìn nhãn nằm dưới ngón tay, và trang bị cuộn khiến nhãn vẫn cứ nằm dưới ngón tay
Tôi muốn biết bạn đang dùng trình duyệt và hệ điều hành nào. Thư viện này dùng input native nên không làm gì liên quan tới việc chặn cuộn
Trên điện thoại iOS của tôi, việc cuộn bị chặn trên mọi phần tử
input[type=range], bất kể có áp dụng style hay không[1] https://github.com/Stanko/dual-range-input/issues
Phần gọi là “native” còn có thể tranh luận. Ý là thư viện dùng hai HTML range input native, nên giữ được tương tác native và các tính năng accessibility, nhưng tiêu chí của tôi thì rõ ràng
Nếu để hoạt động như dự định mà cần JavaScript, thì nó không còn là native nữa
Dù vậy tôi hiểu ý tác giả. Vì họ ghép các slider thông thường thay vì tự triển khai một đống div chạy bằng JS, nên đây gần với giải pháp native hơn nhiều. Nếu không thể thay JavaScript bằng CSS khéo léo, có lẽ đây là cách gần nhất có thể
Bất kể có native hay không, loại widget này nếu dùng được thì rất hữu ích, và trong mảng đó còn thiếu implementation tốt
Trước khi thuộc tính
popoverxuất hiện, không có JavaScript thì hoàn toàn không mở đượcNgoài ra cũng có những phương thức JavaScript vanilla native đáng được phân biệt với các thư viện hoàn toàn không dùng công cụ xác thực nội bộ của trình duyệt, như form validation
Tôi đã tìm một thứ như thế này khi làm https://spodj.intermer.net/
Tôi đã phải dùng React khá vụng về để có được chức năng cần thiết, vì tôi không giỏi phát triển front-end