Jak zasymulować najechanie dotykiem w przeglądarkach obsługujących dotyk?

120

W przypadku takiego kodu HTML:

<p>Some Text</p>

Następnie trochę CSS takich jak ten:

p {
  color:black;
}

p:hover {
  color:red;
}

Jak mogę pozwolić, aby długie dotknięcie urządzenia z obsługą dotykową replikowało kursor? Mogę zmienić znaczniki / użyć JS itp., Ale nie mogę wymyślić prostego sposobu na zrobienie tego.

Rich Bradshaw
źródło
Myślę w szczególności o systemie iPhone OS - jest to demonstracja animacji CSS, na której dla wielu osób łatwo jest uruchomić kursor, a nie kliknięcia.
Rich Bradshaw

Odpowiedzi:

172

OK, już to wymyśliłem! Polega na nieznacznej zmianie CSS i dodaniu JS.

Korzystanie z jQuery, aby to ułatwić:

$(document).ready(function() {
    $('.hover').on('touchstart touchend', function(e) {
        e.preventDefault();
        $(this).toggleClass('hover_effect');
    });
});

Po angielsku: kiedy zaczynasz lub kończysz dotyk, włącz hover_effectlub wyłącz zajęcia .

Następnie w kodzie HTML dodaj kursor klasy do wszystkiego, z czym chcesz, aby to działało. W swoim CSS zamień dowolne wystąpienie:

element:hover {
    rule:properties;
}

z

element:hover, element.hover_effect {
    rule:properties;
}

Aby zwiększyć użyteczność, dodaj to również do swojego CSS:

.hover {
-webkit-user-select: none;
-webkit-touch-callout: none;        
}

Aby zatrzymać przeglądarkę z pytaniem o skopiowanie / zapisanie / wybranie obrazu lub cokolwiek.

Łatwo!

Rich Bradshaw
źródło
To jest świetne. Podzieliłem jednak funkcję domready, ponieważ togglewszystko zepsuje, jeśli użytkownik dotknie jednego elementu i przeciągnie się na inny (np. Jeśli dotknął niewłaściwego elementu i próbuje anulować dotyk)
Jacksonkr
Usunąłem touchendi preventDefault()na mojej stronie internetowej i działa dobrze, ale teraz menu nie są automatycznie zamykane przez dotknięcie poza celem.
Даниил Пронин
Chciałbym również uzyskać porady, jak zamknąć takie menu, gdy użytkownik dotknie innego miejsca lub zacznie przewijać. Wydaje mi się, że touchstartna korpusie może być inny słuchacz (lub podobny), ale zastanawiałem się, czy jest lepszy sposób. Dzięki!
Darryl Young
2
Czy w ogóle można zignorować gest przewijania? Robi dokładnie to, czego potrzebuję, ale teraz nie mogę przewijać zawartości, która ma ten efekt.
podobno
1
Usunąłem PrevaultDefault, ponieważ chcę, aby przewijanie było dostępne, ale dzięki za wskazówki dotyczące Touchstart i Touchchend! Niech Bóg posyła to jedyna odpowiedź, która niezawodnie działa we wszystkich przeglądarkach.
Petraeus
54

Wszystko, co musisz zrobić, to nawiązać kontakt z rodzicem. Coś takiego zadziała:

$('body').on('touchstart', function() {});

Nie musisz nic robić w funkcji, zostaw ją pustą. To wystarczy, aby unosić się przy dotyku, więc dotyk zachowuje się bardziej jak: najechanie, a mniej jak: aktywny. Magia iOS.

Razvan Cercelaru
źródło
2
Czy to rozwiązanie pozwala na wykonanie linku przy drugim dotknięciu?
samuelkobe
1
Nie, nadal powoduje kliknięcie przy pierwszym dotknięciu. Właśnie to przetestowałem.
Jack
Świetne rozwiązanie! Szybko i łatwo.
Hunter Turner,
41

Spróbuj tego:

<script>
document.addEventListener("touchstart", function(){}, true);
</script>

A w swoim CSS:

element:hover, element:active {
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-user-select: none;
-webkit-touch-callout: none /*only to disable context menu on long press*/
}

Z tym kodem nie potrzebujesz dodatkowej klasy .hover!

Anzelm
źródło
1
to działa dla mnie .. kilka innych rzeczy, które mogą pomóc to jeszcze bardziej, to zamiast dodawać javascript, zrób to: <body ontouchstart = ""> następnie dodaj mały licznik czasu w css: active event: tr: active {background-color : # 118aab; przejście: wszystkie .2s liniowe; -webkit-tap-highlight-color: rgba (0,0,0,0); -webkit-user-select: brak; -webkit-touch-callout: none}
Kozy
Pracował dla mnie. Dzięki ... Jedyna rzecz - trochę się opóźnia ... To znaczy - po naciśnięciu nie ładuje się natychmiast.
Roman Losev
Użyj fastclick.js, aby usunąć opóźnienie
Anselm
25

Aby odpowiedzieć na główne pytanie: „Jak symulować najechanie kursorem za pomocą dotyku w przeglądarkach obsługujących dotyk?”

Po prostu zezwól na „kliknięcie” elementu (dotykając ekranu), a następnie wywołaj hoverzdarzenie za pomocą JavaScript.

var p = document.getElementsByTagName('p')[0];
p.onclick = function() {
 // Trigger the `hover` event on the paragraph
 p.onhover.call(p);
};

To powinno działać, o ile hoverna Twoim urządzeniu jest wydarzenie (nawet jeśli normalnie nie jest używane).

Aktualizacja: właśnie przetestowałem tę technikę na moim iPhonie i wydaje się, że działa dobrze. Wypróbuj tutaj: http://jsfiddle.net/mathias/YS7ft/show/light/

Jeśli zamiast tego chcesz użyć „długiego dotknięcia” do wywołania najechania kursorem, możesz użyć powyższego fragmentu kodu jako punktu wyjścia i bawić się licznikami czasu i innymi rzeczami;)

Mathias Bynens
źródło
Nie wiedziałem o tym onhover.call (p) bit, to całkiem fajne. Nie ma jednak odlotu ...
Rich Bradshaw
Czy przetestowałeś to z wieloma elementami i długim dotykiem? Czy to oznacza, że ​​użytkownik porusza palcem po ekranie i pokazuje styl po najechaniu na każdy element?
Marcus
Wygląda na to, że jeśli używasz już jQuery, możesz po prostu wywołać zdarzenie hover za pomocą samego jQuery, np .: jQuery.hover (); Nie jestem jednak pewien, czy api to obsługuje.
Kzqai
Ups, to był komentarz bardziej pasujący do powyższego postu, który faktycznie używa jQuery, przepraszam.
Kzqai
Czy potrzebujesz specjalnego skryptu lub wywołania funkcji? U mnie działa, ale tylko na jednej stronie i nie wiem, jaka jest różnica.
Richard Young,
13

Dalsze ulepszone rozwiązanie

Najpierw poszedłem z podejściem Richa Bradshawa , ale potem zaczęły pojawiać się problemy. Wykonując e.preventDefault () w zdarzeniu „touchstart” , strona nie jest już przewijana i ani długie naciśnięcie nie jest w stanie uruchomić menu opcji, ani podwójne kliknięcie powiększenia nie jest w stanie zakończyć wykonywania.

Rozwiązaniem może być ustalenie, które zdarzenie jest wywoływane i po prostu e.preventDefault () w późniejszym, „touchend” . Ponieważ „ruch dotykowy” przewijania występuje przed „dotknięciem” , pozostaje on domyślnie, a „kliknięcie” jest również zapobiegane, ponieważ występuje po słowie w łańcuchu zdarzeń stosowanym na telefonie komórkowym, na przykład:

// Binding to the '.static_parent' ensuring dynamic ajaxified content
$('.static_parent').on('touchstart touchend', '.link', function (e) {

    // If event is 'touchend' then...
    if (e.type == 'touchend') {
        // Ensuring we event prevent default in all major browsers
        e.preventDefault ? e.preventDefault() : e.returnValue = false;
    }

    // Add class responsible for :hover effect
    $(this).toggleClass('hover_effect');
});

Ale potem, gdy pojawi się menu opcji, nie uruchamia już „touchchend” odpowiedzialnego za wyłączenie klasy, a następnym razem zachowanie najechania kursorem będzie odwrotne , całkowicie pomieszane.

Rozwiązaniem byłoby więc ponownie warunkowe ustalenie, w którym zdarzeniu się znajdujemy, lub po prostu zrobienie oddzielnych, i użycie addClass () i removeClass () odpowiednio na 'touchstart' i 'touchend' , upewniając się, że zawsze zaczyna się i kończy odpowiednio dodawanie i usuwanie zamiast warunkowego decydowania o tym. Na koniec powiążemy również usuwające wywołanie zwrotne z typem zdarzenia „focusout” , pozostając odpowiedzialnym za wyczyszczenie wszystkich klas linków, które mogą pozostać włączone i nigdy więcej nie wracać, na przykład:

$('.static_parent').on('touchstart', '.link', function (e) {
    $(this).addClass('hover_effect');
});

$('.static_parent').on('touchend focusout', '.link', function (e) {
    // Think double click zoom still fails here
    e.preventDefault ? e.preventDefault() : e.returnValue = false;
    $(this).removeClass('hover_effect');
});

Uwaga: niektóre błędy nadal występują w dwóch poprzednich rozwiązaniach, a także myślę (nie testowano), powiększanie dwukrotnym kliknięciem nadal nie działa.

Schludne i miejmy nadzieję wolne od błędów (nie :)) rozwiązanie JavaScript

Teraz , przez sekundę, czystsze, uporządkowane i responsywne podejście, po prostu używając javascript (bez mieszania klasy .hover i pseudo: hover) i skąd możesz bezpośrednio wywołać swoje zachowanie Ajax w uniwersalnym (mobilnym i stacjonarnym) zdarzeniu „kliknięcie” , Znalazłem dość dobrze udzielone pytanie, z którego w końcu zrozumiałem, jak mogę mieszać zdarzenia dotyku i myszy bez kilku wywołań zwrotnych zdarzeń nieuchronnie zmieniających się nawzajem w łańcuchu zdarzeń. Oto jak:

$('.static_parent').on('touchstart mouseenter', '.link', function (e) {
    $(this).addClass('hover_effect');
});

$('.static_parent').on('mouseleave touchmove click', '.link', function (e) {
    $(this).removeClass('hover_effect');

    // As it's the chain's last event we only prevent it from making HTTP request
    if (e.type == 'click') {
        e.preventDefault ? e.preventDefault() : e.returnValue = false;

        // Ajax behavior here!
    }
});
Alexandre Rosário
źródło
3

Efekt myszy hovernie może być zaimplementowany w urządzeniu dotykowym. Kiedy pojawiła się taka sama sytuacja safari ios, :activew css zastosowałem efekt.

to znaczy.

p:active {
  color:red;
}

W moim przypadku to działa. Może tak jest również w przypadku, którego można używać bez użycia javascript. Po prostu spróbuj.

sijo vijayan
źródło
2

Dodaj ten kod, a następnie ustaw klasę „tapHover” na elementy, na których chciałbyś pracować w ten sposób. Gdy pierwszy raz dotkniesz elementu, uzyska on pseudoklasę „: hover” i klasę „tapped”. Zdarzenie kliknięcia zostanie zablokowane. Drugie dotknięcie tego samego elementu - zostanie uruchomione zdarzenie kliknięcia.

// Activate only in devices with touch screen
if('ontouchstart' in window)
{
    // this will make touch event add hover pseudoclass
    document.addEventListener('touchstart', function(e) {}, true);

    // modify click event
    document.addEventListener('click', function(e) {
        // get .tapHover element under cursor
        var el = jQuery(e.target).hasClass('tapHover')
            ? jQuery(e.target)
            : jQuery(e.target).closest('.tapHover');

        if(!el.length)
            return;

        // remove tapped class from old ones
        jQuery('.tapHover.tapped').each(function() {
            if(this != el.get(0))
                jQuery(this).removeClass('tapped');
        });

        if(!el.hasClass('tapped'))
        {
            // this is the first tap
            el.addClass('tapped');
            e.preventDefault();
            return false;
        }
        else
        {
            // second tap
            return true;
        }
    }, true);
}
.box {
	float:		left;
	
	display:	inline-block;
	margin:		50px 0 0 50px;
	width:		100px;
	height:		100px;
	overflow:	hidden;
	
	font-size:	20px;
	
	border:		solid 1px black;
}
.box.tapHover {
	background:	yellow;
}
.box.tapped {
	border:		solid 3px red;
}
.box:hover {
	background:	red;
}
<div class="box" onclick="this.innerHTML = Math.random().toFixed(5)"></div>
<div class="box tapHover" onclick="this.innerHTML = Math.random().toFixed(5)"></div>
<div class="box tapHover" onclick="this.innerHTML = Math.random().toFixed(5)"></div>

l00k
źródło
1

Bez konkretnego urządzenia (a raczej przeglądarki) JS Jestem prawie pewien, że nie masz szczęścia.

Edycja: myślę, że chcesz tego uniknąć, dopóki nie przeczytałem ponownie Twojego pytania. W przypadku Mobile Safari możesz zarejestrować się, aby otrzymywać wszystkie zdarzenia dotykowe podobne do tego, co możesz zrobić z natywnymi UIView-s. Nie mogę teraz znaleźć dokumentacji, ale spróbuję.

Joonas Trussmann
źródło
1

Jednym ze sposobów byłoby wykonanie efektu najechania po rozpoczęciu dotyku, a następnie usunięcie efektu najechania, gdy dotyk się porusza lub kończy.

Oto, co Apple ma do powiedzenia na temat obsługi dotykowej w ogóle, skoro wspominasz o iPhonie.

ciągnięty do przodu
źródło
Czy treść linku jest nadal aktualna?
Flavien Volken
1

Moim osobistym gustem jest również przypisywanie :hoverstylów do :focusstanu, na przykład:

p {
    color: red;
}

p:hover, p:focus {
    color: blue;
}

Następnie z następującym kodem HTML:

<p id="some-p-tag" tabindex="-1">WOOOO</p>

Oraz następujący JavaScript:

$("#some-p-tag").on("touchstart", function(e){
    e.preventDefault();
    var $elem = $(this);

    if($elem.is(":focus")) {
        //this can be registered as a "click" on a mobile device, as it's a double tap
        $elem.blur()
    }
    else {
        $elem.focus();
    }
});
pimbrouwers
źródło
1

Rozwiązany 2019 - Najedź na dotyk

Wydaje się, że najlepiej jest unikać używania dymka razem z iOS lub dotykiem. Poniższy kod stosuje twój CSS tak długo, jak długo utrzymywany jest dotyk i bez innych menu wysuwanych iOS. Zrób to;

  1. Jquery add: $ ("p"). On ("touchstart", function (e) {$ (this) .focus (); e.preventDefault ();});

  2. CSS: zamień p: hover na p: focus i dodaj p: active

Opcje;

  • zastąp selektor jquery p dowolną klasą itp

  • aby efekt pozostał, trzymaj p: hover również i dodaj body {kursor: ponter;} więc dotknięcie w dowolnym miejscu go kończy

  • wypróbuj zdarzenia click & mouseover, a także touchstart w tym samym kodzie (ale nie testowane)

  • usuń e.preventDefault (); aby umożliwić użytkownikom korzystanie z wysuwanych okienek iOS, np. kopiowania

Uwagi

  • testowane tylko dla elementów tekstowych, system iOS może inaczej traktować dane wejściowe itp

  • testowano tylko na iPhonie XR ios 12.1.12 i iPadzie 3 ios 9.3.5 przy użyciu przeglądarki Safari lub Chrome.

Darren
źródło
0

Połączenie natywnego Javascript i jQuery:

var gFireEvent = function (oElem,sEvent) 
{
 try {
 if( typeof sEvent == 'string' && o.isDOM( oElem ))
 {
  var b = !!(document.createEvent),
     evt = b?document.createEvent("HTMLEvents"):document.createEventObject();
  if( b )    
  {  evt.initEvent(sEvent, true, true ); 
    return !oElem.dispatchEvent(evt);
  }
  return oElem.fireEvent('on'+sEvent,evt);
 }
 } catch(e) {}
 return false;
};


// Next you can do is (bIsMob etc you have to determine yourself):

   if( <<< bIsMob || bIsTab || bisTouch >>> )
   {
     $(document).on('mousedown', function(e)
     {
       gFireEvent(e.target,'mouseover' );
     }).on('mouseup', function(e)
     {
       gFireEvent(e.target,'mouseout' );
     });
   }
Codebeat
źródło
1
przepraszam, że jestem pedantyczny, ale jquery to javacript
matpol
2
@matpol: jQuery to javascript, ale javascript to nie jQuery. Specjalnie dla Ciebie dodaję słowo „czysty”, czysty javascript. Zadowolony panie?
Codebeat
nie możesz używać jquery bez „czystego” javascript. Podkreślam tylko tę kwestię, ponieważ niektórzy ludzie, którzy często tak robią, nie wydają się o tym wiedzieć.
matpol
5
Połączenie natywnego Javascript i jQuery, Zadowolony?
Codebeat
0

Najłatwiejsze rozwiązanie, jakie znalazłem: miałem kilka tagów <span> z regułami css: hover. Przełączyłem się na <a href = "javascript: void (0)"> i voilà. Style najeżdżania w iOS zaczęły działać.

Becario Senior
źródło
0

Używaj również CSS, dodawaj fokus i aktywny (dla IE7 i poniżej) do ukrytego linku. Przykład menu ul wewnątrz elementu DIV z menu klas:

.menu ul ul {display:none; position:absolute; left:100%; top:0; padding:0;}
.menu ul ul ul {display:none; position:absolute; top:0; left:100%;}
.menu ul ul a, .menu ul ul a:focus, .menu ul ul a:active { width:170px; padding:4px 4%; background:#e77776; color:#fff; margin-left:-15px; }
.menu ul li:hover > ul { display:block; }
.menu ul li ul li {margin:0;}

Jest późno i niesprawdzone, powinno działać ;-)

Mark Groen
źródło