Có thể buộc bỏ qua: di chuột giả cổ cho người dùng iPhone / iPad không?


94

Tôi có một số menu css trên trang web của mình mở rộng với :hover(không có js)

Điều này hoạt động theo cách bán đứt quãng trên iDevices, ví dụ: một lần nhấn sẽ kích hoạt :hoverquy tắc và mở rộng menu, nhưng sau đó nhấn vào nơi khác không xóa :hover. Ngoài ra, nếu có một liên kết bên trong phần tử bị :hover'ed, bạn phải nhấn hai lần để kích hoạt liên kết (lần nhấn đầu tiên kích hoạt :hover, lần nhấn thứ hai liên kết kích hoạt).

Tôi đã có thể làm cho mọi thứ hoạt động tốt trên iPhone bằng cách ràng buộc touchstartsự kiện.

Vấn đề là đôi khi safari di động vẫn chọn kích hoạt :hoverquy tắc từ css thay vì các touchstartsự kiện của tôi !

Tôi biết đây là vấn đề vì khi tôi tắt tất cả các :hoverquy tắc theo cách thủ công trong css, safari di động hoạt động tốt (nhưng các trình duyệt thông thường rõ ràng là không còn nữa).

Có cách nào để tự động "hủy bỏ" :hovercác quy tắc cho các phần tử nhất định khi người dùng đang sử dụng safari trên thiết bị di động không?

Xem và so sánh hành vi iOS tại đây: http://jsfiddle.net/74s35/3/ Lưu ý: chỉ một số thuộc tính css mới kích hoạt hành vi hai lần nhấp, ví dụ: display: none; nhưng không phải nền: đỏ; hoặc văn bản-trang trí: gạch chân;


7
VUI LÒNG KHÔNG vô hiệu hóa tính năng di chuột chỉ vì sự hiện diện của các sự kiện 'chạm'. Điều này sẽ ảnh hưởng không tốt đến người dùng máy tính xách tay có thiết bị đầu vào cảm ứng và chuột. Xem câu trả lời của tôi để biết thêm chi tiết
Simon_Weaver,

Câu trả lời:


77

Tôi thấy rằng ": hover" không thể đoán trước được trong Safari của iPhone / iPad. Đôi khi chạm vào phần tử làm cho phần tử đó trở thành ": hover", trong khi đôi khi nó chuyển sang phần tử khác.

Còn hiện tại, tôi chỉ có một lớp body “không đụng hàng”.

<body class="yui3-skin-sam no-touch">
   ...
</body>

Và có tất cả các quy tắc CSS với ": hover" bên dưới ".no-touch":

.no-touch my:hover{
   color: red;
}

Ở đâu đó trong trang, tôi có javascript để xóa lớp không chạm khỏi cơ thể.

if ('ontouchstart' in document) {
    Y.one('body').removeClass('no-touch');
}

Điều này trông không hoàn hảo, nhưng nó vẫn hoạt động.


5
Đây là cách duy nhất để làm điều đó. Điều làm tôi khó chịu là nó gây ô nhiễm trang web "bình thường" với những thứ không thuộc về đó và không liên quan. Ồ tốt. Cảm ơn.
Christopher Camps

Bạn cũng có thể sử dụng các truy vấn phương tiện truyền thông với một dự phòng cho IE
Lime

2
@Shackrock: Tôi không nghĩ cách tiếp cận "một lần chạm để di chuột và hai lần để nhấp" là một giải pháp tốt. Vì trên thực tế, không có thao tác di chuột thực tế nào trên thiết bị cảm ứng (cho đến khi chúng đi kèm với màn hình cảm nhận ngón tay của bạn di chuột trên thiết bị đó), tôi nghĩ tốt hơn là không nên mô phỏng di chuột. Đối với các hiệu ứng, giống như những hiệu ứng phổ biến trên các nút và liên kết, chỉ cần bỏ qua hiệu ứng. Đối với các menu mở rộng khi di chuột, thay vào đó, hãy mở rộng chúng khi nhấn / nhấp. 2c của tôi.
Adrian Schmidt

18
Tôi đã tìm thấy các vấn đề thời gian gần đây trong loại này của phương pháp do mới 8 hệ thống Windows có tính năng cảm ứng và chuột đầu vào
Tom

4
+1 cho nhận xét của Tom - kiểm tra ontouchstart sẽ trả về true trên máy tính xách tay có khả năng cảm ứng, ngay cả khi được cắm vào màn hình bên ngoài (nơi trang web thân thiện với chuột có trạng thái di chuột được mong muốn hơn).
gregdev

45

:hoverkhông phải là vấn đề ở đây. Safari cho iOS tuân theo một quy tắc rất kỳ quặc. Nó cháy mouseovermousemoveđầu tiên; nếu bất kỳ điều gì được thay đổi trong các sự kiện này, 'nhấp chuột' và các sự kiện liên quan sẽ không bị kích hoạt:

Sơ đồ sự kiện chạm trong iOS

mouseentermouseleavedường như được bao gồm, mặc dù chúng không được chỉ định trong biểu đồ.

Nếu bạn sửa đổi bất kỳ điều gì do các sự kiện này, các sự kiện nhấp chuột sẽ không được kích hoạt. Điều đó bao gồm một cái gì đó cao hơn trong cây DOM. Ví dụ: điều này sẽ ngăn các nhấp chuột đơn lẻ hoạt động trên trang web của bạn với jQuery:

$(window).on('mousemove', function() {
    $('body').attr('rel', Math.random());
});

Chỉnh sửa: Để làm rõ, hoversự kiện của jQuery bao gồm mouseentermouseleave. Cả hai điều này sẽ ngăn chặn clicknếu nội dung bị thay đổi.


5
tất nhiên: di chuột là vấn đề :-) hoặc đúng hơn là việc triển khai lỗi của Safari về nó. Đây là thông tin cơ bản thú vị về vấn đề này, nhưng Apple là bên duy nhất có thể khắc phục hành vi khủng khiếp này. nó cần phải 'di chuột qua' khi một cái gì đó bên ngoài các phần tử được nhấp vào, hoặc không bao giờ 'di chuột' ở vị trí đầu tiên. hành vi hiện tại về cơ bản phá vỡ bất kỳ trang web mà sử dụng: hover cho một con chuột
Simon_Weaver

điều này đã giúp tôi nhận ra rằng hovertrình xử lý jquery đang ngăn một clicktrình xử lý riêng biệt bị đánh - thật là một trò đùa!
Simon_Weaver

1
Câu trả lời tuyệt vời và lời giải thích tốt nhất về vấn đề mà tôi đã gặp phải. Không chỉ CSS di chuột gây ra vấn đề "nhấp đúp", mà theo nghĩa đen là bất kỳ sửa đổi DOM nào sau sự kiện mouseover / mouseenter et al.
Oliver Joseph Ash

Dưới đây là một ví dụ cho thấy sự kiện nhấp chuột không bắn khi DOM được sửa đổi trên mouseenter jsbin.com/maseti/5/edit?html,js,output
Oliver Joseph Ash

@Oliver Joseph Ash Right, do đó là ví dụ JS trong câu trả lời. ;)
Zenexer

18

Thư viện phát hiện tính năng của trình duyệt Modernizer bao gồm kiểm tra các sự kiện chạm.

Hành vi mặc định là áp dụng các lớp cho phần tử html của bạn cho mỗi tính năng được phát hiện. Sau đó, bạn có thể sử dụng các lớp này để tạo kiểu cho tài liệu của mình.

Nếu sự kiện cảm ứng không được bật Modernizr có thể thêm một lớp no-touch:

<html class="no-touch">

Và sau đó phạm vi các kiểu di chuột của bạn với lớp này:

.no-touch a:hover { /* hover styles here */ }

Bạn có thể tải xuống bản dựng Modernizr tùy chỉnh để bao gồm ít hoặc nhiều tính năng phát hiện nếu bạn cần.

Dưới đây là ví dụ về một số lớp có thể được áp dụng:

<html class="js no-touch postmessage history multiplebgs
             boxshadow opacity cssanimations csscolumns cssgradients
             csstransforms csstransitions fontface localstorage sessionstorage
             svg inlinesvg no-blobbuilder blob bloburls download formdata">

Lưu ý rằng trên các phiên bản gần đây của Chrome trên máy tính để bàn mà Modernizr báo cáo là "chạm". Điều này rõ ràng đã được sửa chữa trong phiên bản 3 với cảm ứng.
Craig Jacobs

18

Một số thiết bị (như những người khác đã nói) có cả sự kiện cảm ứng và chuột. Ví dụ, Microsoft Surface có màn hình cảm ứng, bàn di chuột VÀ bút cảm ứng thực sự làm tăng các sự kiện di chuột khi nó được di chuột lên trên màn hình.

Bất kỳ giải pháp nào vô hiệu hóa :hoverdựa trên sự hiện diện của các sự kiện 'chạm' cũng sẽ ảnh hưởng đến người dùng Surface (và nhiều thiết bị tương tự khác). Nhiều máy tính xách tay mới có tính năng cảm ứng và sẽ phản ứng với các sự kiện chạm - vì vậy việc tắt di chuột là một việc thực sự tồi.

Đây là một lỗi trong Safari, hoàn toàn không có lời biện minh nào cho hành vi khủng khiếp này. Tôi từ chối phá hoại các trình duyệt không phải iOS vì một lỗi trong iOS Safari dường như đã có trong nhiều năm. Tôi thực sự hy vọng họ sẽ sửa lỗi này cho iOS8 vào tuần tới nhưng trong thời gian chờ đợi ....

Giải pháp của tôi :

Một số đã đề xuất sử dụng Modernizr rồi, Modernizr cho phép bạn tạo các bài kiểm tra của riêng mình. Về cơ bản, những gì tôi đang làm ở đây là 'trừu tượng hóa' ý tưởng về một trình duyệt hỗ trợ :hoverthành một bài kiểm tra Modernizr mà tôi có thể sử dụng trong suốt mã của mình mà không cần mã cứng if (iOS).

 Modernizr.addTest('workinghover', function ()
 {
      // Safari doesn't 'announce' to the world that it behaves badly with :hover
      // so we have to check the userAgent  
      return navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? false : true;
 });

Sau đó, css trở thành một cái gì đó như thế này

html.workinghover .rollover:hover 
{
    // rollover css
}

Chỉ trên iOS, thử nghiệm này sẽ không thành công và vô hiệu hóa quá trình di chuyển.

Phần tốt nhất của sự trừu tượng đó là nếu tôi thấy nó bị hỏng trên một Android nhất định hoặc nếu nó được sửa trong iOS9 thì tôi chỉ cần sửa đổi thử nghiệm.


Giải pháp này vừa khó chịu vừa là giải pháp tốt nhất hiện có. Nó chỉ bị hỏng nếu một trình duyệt gặp khó khăn với di chuột có cả chạm và nhấp, nhưng đối với các trình duyệt chỉ cảm ứng trên ipad / iphone thì điều này không đúng. Đề xuất của tôi là chỉ thực hiện điều này như một bản sửa lỗi tối ưu hóa (để loại bỏ hiện tượng nhấp nháy khi di chuột qua khi đã sử dụng fastclick.js) và đảm bảo mọi thứ cũng hoạt động mà không có nó.
Gersom,

Cảm ơn. Tôi đồng ý. Tò mò muốn biết iOS 9 làm gì cho vấn đề này - nếu có.
Simon_Weaver

Tôi đã đi theo hướng ngược lại:html:not(.no-hover) .category:hover { ...
Chris Marisic

1 cho "Safari không 'thông báo' với thế giới rằng nó cư xử tồi tệ với: hover" Vấn đề này làm cho iOS cách tồi tệ hơn bất kỳ phiên bản của IE trong quan điểm của tôi ...
Spencer O'Reilly

16

Việc thêm thư viện FastClick vào trang của bạn sẽ khiến tất cả các lần nhấn trên thiết bị di động được chuyển thành các sự kiện nhấp chuột (bất kể người dùng nhấp vào đâu), do đó, nó cũng sẽ khắc phục sự cố di chuột trên thiết bị di động. Tôi đã chỉnh sửa fiddle của bạn làm ví dụ: http://jsfiddle.net/FvACN/8/ .

Chỉ cần bao gồm lib fastclick.min.js trên trang của bạn và kích hoạt qua:

FastClick.attach(document.body);

Như một lợi ích phụ, nó cũng sẽ loại bỏ độ trễ onClick 300ms khó chịu mà các thiết bị di động phải chịu.


Có một số hậu quả nhỏ khi sử dụng FastClick có thể có hoặc có thể không quan trọng đối với trang web của bạn:

  1. Nếu bạn chạm vào một nơi nào đó trên trang, cuộn lên, cuộn xuống, rồi thả ngón tay của bạn ở vị trí chính xác mà bạn đã đặt nó ban đầu, FastClick sẽ hiểu đó là một "cú nhấp chuột", mặc dù rõ ràng là không. Ít nhất đó là cách nó hoạt động trong phiên bản FastClick mà tôi hiện đang sử dụng (1.0.0). Ai đó có thể đã khắc phục sự cố kể từ phiên bản đó.
  2. FastClick loại bỏ khả năng ai đó "nhấp đúp".

1
Bạn có sẵn lòng chia sẻ một ví dụ cụ thể khác về cách tôi có thể làm điều này cho trang web của mình không. Tôi không biết gì về javascript nên tôi hơi bối rối về cách thức hoạt động của nó. Tôi có tất cả các liên kết này trên trang web của mình mà người dùng thường di chuột qua trước khi nhấp vào, nhưng đối với ipad / di động, tôi muốn tạo liên kết này để họ không phải nhấp hai lần.
Andy

@Andy - Không rõ chính xác bạn cần gì và thiếu gì ... Bạn đã thử những gì, cho đến nay và bạn đang gặp lỗi nào, nếu có? Có lẽ tốt nhất là bạn nên tạo một câu hỏi Stackoverflow mới, nếu bạn chưa có, với một ví dụ về những gì bạn đang cố gắng làm (?) Hoặc cố gắng làm rõ nó là gì trong ví dụ jsfiddle ở trên mà bạn không nên làm ' t hiểu.
Troy

cái này có thể được sử dụng trong các ứng dụng trang đơn không? Ý tôi là khi nội dung DOM được cập nhật mọi
Sebastien Lorber

Có, tôi đang sử dụng nó với các ứng dụng một trang.
Troy

16

Một giải pháp tốt hơn mà không cần kiểm tra JS, lớp css và chế độ xem: bạn có thể sử dụng Tính năng phương tiện tương tác (Truy vấn phương tiện cấp 4)

Như thế này:

@media (hover) {
  // properties
  my:hover {
    color: red;
  }
}

iOS Safari hỗ trợ nó

Thông tin thêm về: https://www.jonathanfielding.com/an-introduction-to-interaction-media-features/


Tuy nhiên, không được hỗ trợ bởi Firefox (tất nhiên tại thời điểm nhận xét này)
Frank

Tính năng này hiện cũng được Firefox hỗ trợ (FF 64+, phát hành vào tháng 12 năm 2018).
Wunch vào

4

Về cơ bản có ba tình huống:

  1. Người dùng chỉ có thiết bị chuột / con trỏ và có thể kích hoạt:hover
  2. Người dùng chỉ có màn hình cảm ứng và không thể kích hoạt :hovercác phần tử
  3. Người dùng có cả màn hình cảm ứng và thiết bị con trỏ

Câu trả lời ban đầu chấp nhận làm việc tuyệt vời nếu chỉ có hai kịch bản đầu tiên là có thể, nơi người dùng có một trong hai con trỏ hoặc màn hình cảm ứng. Điều này đã phổ biến khi OP đặt câu hỏi 4 năm trước. Một số người dùng đã chỉ ra rằng các thiết bị Windows 8 và Surface đang có nhiều khả năng xảy ra trường hợp thứ ba hơn.

Giải pháp iOS cho vấn đề không thể di chuột trên các thiết bị màn hình cảm ứng (như được nêu chi tiết bởi @Zenexer) là thông minh, nhưng có thể khiến mã đơn giản hoạt động sai (theo ghi nhận của OP). Chỉ tắt di chuột cho các thiết bị màn hình cảm ứng có nghĩa là bạn vẫn cần phải viết mã một giải pháp thay thế thân thiện với màn hình cảm ứng. Phát hiện khi người dùng sử dụng cả con trỏ và màn hình cảm ứng để làm mờ vùng nước (như được giải thích bởi @Simon_Weaver).

Tại thời điểm này, giải pháp an toàn nhất là tránh sử dụng :hovernhư cách duy nhất để người dùng có thể tương tác với trang web của bạn. Hiệu ứng di chuột là một cách tốt để chỉ ra rằng một liên kết hoặc nút có thể thực hiện được, nhưng người dùng không cần phải di chuột qua một phần tử để thực hiện một hành động trên trang web của bạn.

Suy nghĩ lại về chức năng "di chuột" với màn hình cảm ứng có một cuộc thảo luận tốt về các phương pháp tiếp cận UX thay thế. Các giải pháp được cung cấp bởi câu trả lời ở đó bao gồm:

  • Thay thế menu di chuột bằng các hành động trực tiếp (liên kết luôn hiển thị)
  • Thay thế menu khi di chuột bằng menu khi chạm
  • Di chuyển một lượng lớn nội dung khi di chuột vào một trang riêng biệt

Trong tương lai, đây có lẽ sẽ là giải pháp tốt nhất cho tất cả các dự án mới. Câu trả lời được chấp nhận có lẽ là giải pháp tốt thứ hai, nhưng hãy đảm bảo tính đến các thiết bị cũng có thiết bị con trỏ. Hãy cẩn thận để không loại bỏ chức năng khi thiết bị có màn hình cảm ứng chỉ để giải quyết vấn đề :hoverhack của iOS .


1

Phiên bản JQuery trong .css của bạn sử dụng .no-touch .my-element: hover cho tất cả các quy tắc di chuột của bạn bao gồm JQuery và tập lệnh sau

function removeHoverState(){
    $("body").removeClass("no-touch");
}

Sau đó, trong thẻ body, hãy thêm class = "no-touch" ontouchstart = "removeHoverState ()"

ngay sau khi khởi động ontouchstart kích hoạt lớp cho tất cả các trạng thái di chuột sẽ bị xóa


0

Thay vì chỉ có hiệu ứng di chuột khi không có cảm ứng, tôi đã tạo một hệ thống để xử lý các sự kiện chạm và điều đó đã giải quyết được vấn đề cho tôi. Đầu tiên, tôi đã xác định một đối tượng để thử nghiệm cho các sự kiện "chạm" (tương đương với "nhấp").

touchTester = 
{
    touchStarted: false
   ,moveLimit:    5
   ,moveCount:    null
   ,isSupported:  'ontouchend' in document

   ,isTap: function(event)
   {
      if (!this.isSupported) {
         return true;
      }

      switch (event.originalEvent.type) {
         case 'touchstart':
            this.touchStarted = true;
            this.moveCount    = 0;
            return false;
         case 'touchmove':
            this.moveCount++;
            this.touchStarted = (this.moveCount <= this.moveLimit);
            return false;
         case 'touchend':
            var isTap         = this.touchStarted;
            this.touchStarted = false;
            return isTap;
         default:
            return true;
      }
   }
};

Sau đó, trong trình xử lý sự kiện của tôi, tôi làm điều gì đó như sau:

$('#nav').on('click touchstart touchmove touchend', 'ul > li > a'
            ,function handleClick(event) {
               if (!touchTester.isTap(event)) {
                  return true;
               }

               // touch was click or touch equivalent
               // nromal handling goes here.
            });

0

Cảm ơn @Morgan Cheng về câu trả lời, tuy nhiên, tôi đã sửa đổi một chút hàm JS để nhận được " touchstart " (mã lấy từ câu trả lời @Timothy Perez ), tuy nhiên, bạn cần jQuery 1.7+ cho việc này

  $(document).on({ 'touchstart' : function(){
      //do whatever you want here
    } });

0

Với phản hồi do Zenexer cung cấp, một mẫu không yêu cầu thẻ HTML bổ sung là:

jQuery('a').on('mouseover', function(event) {
    event.preventDefault();
    // Show and hide your drop down nav or other elem
});
jQuery('a').on('click', function(event) {
    if (jQuery(event.target).children('.dropdown').is(':visible') {
        // Hide your dropdown nav here to unstick
    }
});

Phương thức này kích hoạt lần di chuột trước, lần nhấp thứ hai.


0

Tôi đồng ý là tắt di chuột để chạm là cách tốt nhất.

Tuy nhiên, để tránh rắc rối khi viết lại css của bạn, chỉ cần gói bất kỳ :hovermục nào vào @supports not (-webkit-overflow-scrolling: touch) {}

.hover, .hover-iOS {
  display:inline-block;
  font-family:arial;
  background:red;
  color:white;
  padding:5px;
}
.hover:hover {
  cursor:pointer;
  background:green;
}

.hover-iOS {
  background:grey;
}

@supports not (-webkit-overflow-scrolling: touch) {
  .hover-iOS:hover {
    cursor:pointer;
    background:blue;
  }

}
<input type="text" class="hover" placeholder="Hover over me" />

<input type="text" class="hover-iOS" placeholder="Hover over me (iOS)" />


0

Đối với những người có trường hợp sử dụng phổ biến là vô hiệu hóa :hoversự kiện trên iOS Safari, cách đơn giản nhất là sử dụng truy vấn phương tiện chiều rộng tối thiểu cho các :hoversự kiện của bạn nằm trên chiều rộng màn hình của thiết bị bạn đang tránh. Thí dụ:

@media only screen and (min-width: 1024px) {
  .my-div:hover { // will only work on devices larger than iOS touch-enabled devices. Will still work on touch-enabled PCs etc.
    background-color: red;
  }
}

-6

Chỉ cần nhìn vào kích thước màn hình….

@media (min-width: 550px) {
    .menu ul li:hover > ul {
    display: block;
}
}

-12

đó là mã bạn muốn đặt nó vào

// a function to parse the user agent string; useful for 
// detecting lots of browsers, not just the iPad.
function checkUserAgent(vs) {
    var pattern = new RegExp(vs, 'i');
    return !!pattern.test(navigator.userAgent);
}
if ( checkUserAgent('iPad') ) {
    // iPad specific stuff here
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.