2014-08-29 17 views
30

Tôi đang làm việc trên một số giao diện người dùng javascript và sử dụng rất nhiều sự kiện chạm như 'touchend' để cải thiện phản hồi trên thiết bị cảm ứng. Tuy nhiên, có một số vấn đề logic đang làm tôi khó chịu ...Tiếp xúc JavaScript so với tiến trình nhấp chuột

Tôi đã thấy nhiều nhà phát triển trộn lẫn 'touchend' và 'click' trong cùng một sự kiện. Trong nhiều trường hợp nó sẽ không làm tổn thương, nhưng về cơ bản các chức năng sẽ bắn hai lần trên các thiết bị cảm ứng:

button.on('click touchend', function(event) { 
    // this fires twice on touch devices 
}); 

Nó đã được gợi ý rằng người ta có thể phát hiện khả năng cảm ứng, và thiết lập sự kiện này một cách thích hợp ví dụ:

var myEvent = ('ontouchstart' in document.documentElement) ? 'touchend' : 'click'; 
button.on(myEvent, function(event) { 
    // this fires only once regardless of device 
}); 

Vấn đề với ở trên, là nó sẽ phá vỡ trên các thiết bị hỗ trợ cả cảm ứng và chuột. Nếu người dùng hiện đang sử dụng chuột trên thiết bị đầu vào kép, 'nhấp' sẽ không kích hoạt vì chỉ có 'touchend' được gán cho nút.

Một giải pháp khác là phát hiện thiết bị (ví dụ: "iOS") và chỉ định một sự kiện dựa trên điều đó: Click event called twice on touchend in iPad. Tất nhiên, giải pháp trong liên kết ở trên chỉ dành cho iOS (không phải Android hoặc các thiết bị khác), và có vẻ giống như một "hack" để giải quyết một cái gì đó khá tiểu học.

Một giải pháp khác là phát hiện chuyển động của chuột và kết hợp nó với khả năng cảm ứng để tìm hiểu xem người dùng có đang sử dụng chuột hay chạm không. Vấn đề tất nhiên là người dùng có thể không di chuyển chuột từ khi bạn muốn phát hiện nó ...

Giải pháp đáng tin cậy nhất mà tôi có thể nghĩ, là sử dụng hàm đơn giản debounce để chỉ đảm bảo chức năng kích hoạt một lần trong một khoảng thời gian ngắn (ví dụ 100ms):

button.on('click touchend', $.debounce(100, function(event) { 
    // this fires only once on all devices 
})); 

Tôi có thiếu thứ gì đó hoặc có ai có đề xuất nào tốt hơn không?

Edit: Tôi thấy liên kết này sau khi bài viết của tôi, điều này gợi ý một giải pháp tương tự như trên: How to bind 'touchstart' and 'click' events but not respond to both?

+2

Điều đó không giải quyết được gì ... Modernizr chỉ phát hiện 'chạm'. Sự kiện sẽ vẫn kích hoạt hai lần trên các thiết bị cảm ứng. Hoặc nếu bạn gán sự kiện 'chạm' cho các thiết bị cảm ứng (thông qua modernizr), nó sẽ ngừng hoạt động cho người dùng sử dụng chuột trên các thiết bị đầu vào kép giống như nhiều thiết bị windows8. – suncat100

Trả lời

24

Sau một ngày nghiên cứu, tôi đã tìm giải pháp tốt nhất là chỉ cần dính vào nhấp chuột và sử dụng https://github.com/ftlabs/fastclick để xóa độ trễ cảm ứng. Tôi không chắc chắn 100% điều này có hiệu quả như touchend, nhưng không xa ít nhất.

tôi đã tìm ra một cách để vô hiệu hóa kích hoạt các sự kiện hai lần trên liên lạc bằng cách sử dụng stopPropagationpreventDefault, nhưng đây là tinh ranh vì nó có thể gây trở ngại cho cử chỉ cảm ứng khác tùy thuộc vào phần tử nơi nó được áp dụng:

button.on('touchend click', function(event) { 
    event.stopPropagation(); 
    event.preventDefault(); 
    // this fires once on all devices 
}); 

Tôi đang tìm kiếm giải pháp kết hợp touchstart trên một số yếu tố giao diện người dùng, nhưng tôi không thể xem cách kết hợp với nhấp vào ngoài giải pháp ở trên.

+0

có cách nào để phát hiện liệu trình duyệt có hỗ trợ cảm ứng/kết thúc không? – BenRacicot

-1

Có vô hiệu hóa thao tác nhấn đúp (và do đó trì hoãn nhấp chuột) thường là tùy chọn tốt nhất. Và cuối cùng chúng tôi có lời khuyên tốt để làm điều này sẽ soon work on all browsers.

Nếu vì một lý do nào đó, bạn không muốn làm điều đó.Bạn cũng có thể sử dụng UIEvent.sourceCapabilities.firesTouchEvents để bỏ qua một cách rõ ràng click dư thừa. polyfill for this thực hiện điều gì đó tương tự như mã debouncing của bạn.

3

Câu hỏi này được trả lời nhưng có thể cần được cập nhật.

Theo a notice from Google, sẽ không còn bị trễ 300-350ms nữa nếu chúng tôi bao gồm dòng bên dưới trong phần tử <head>.

<meta name="viewport" content="width=device-width"> 

Vậy đó! Và sẽ không có sự khác biệt giữa sự kiện nhấp và chạm nữa!

+0

Thật thú vị. Có lẽ thời gian để thoát khỏi fastclick.js ... Tôi sẽ kiểm tra nó một chút trong các trình duyệt di động. – suncat100

+0

Thẻ meta phải được đặt trong thẻ 'head', không phải thẻ' header'. – James

+1

Điều này không đúng với tôi. – skybondsor

0

Xin chào bạn có thể thực hiện theo cách sau.

function eventHandler(event, selector) { 
    event.stopPropagation(); // Stop event bubbling. 
    event.preventDefault(); // Prevent default behaviour 
    if (event.type === 'touchend') selector.off('click'); // If event type was touch turn off clicks to prevent phantom clicks. 
} 

// Implement 
$('.class').on('touchend click', function(event) { 
    eventHandler(event, $(this)); // Handle the event. 
    // Do somethings... 
}); 
0

chức năng debounce của bạn sẽ trì hoãn việc xử lý mỗi nhấp chuột cho 100 ms:

button.on('click touchend', $.debounce(100, function(event) { 
    // this is delayed a minimum of 100 ms 
})); 

Thay vào đó, tôi đã tạo một hàm cancelDuplicates rằng đám cháy ngay lập tức, nhưng bất kỳ cuộc gọi tiếp theo trong vòng 10 ms sẽ bị hủy bỏ:

function cancelDuplicates(fn, threshhold, scope) { 
    if (typeof threshhold !== 'number') threshhold = 10; 
    var last = 0; 

    return function() { 
     var now = +new Date; 

     if (now >= last + threshhold) { 
      last = now; 
      fn.apply(scope || this, arguments); 
     } 
    }; 
} 

Cách sử dụng:

button.on('click touchend', cancelDuplicates(function(event) { 
    // This fires right away, and calls within 10 ms after are cancelled. 
})); 
Các vấn đề liên quan