Touchscreens sind auf immer mehr Geräten verfügbar, von Smartphones bis hin zu Desktop-Bildschirmen. Ihre App sollte auf Berührungen intuitiv und ansprechend reagieren.
Touchscreens sind auf immer mehr Geräten verfügbar, von Smartphones bis hin zu Desktop-Bildschirmen. Wenn Nutzer mit Ihrer Benutzeroberfläche interagieren, sollte Ihre App auf ihre Berührungen auf intuitive Weise reagieren.
Auf Elementstatus reagieren
Haben Sie schon einmal ein Element auf einer Webseite berührt oder angeklickt und sich gefragt, ob es von der Website tatsächlich erkannt wurde?
Wenn Sie einfach die Farbe eines Elements ändern, wenn Nutzer Teile Ihrer Benutzeroberfläche berühren oder damit interagieren, erhalten sie die grundlegende Gewissheit, dass Ihre Website funktioniert. Das verringert nicht nur den Frust, sondern kann auch für ein schnelles und responsives Gefühl sorgen.
DOM-Elemente können einen der folgenden Status übernehmen: „Standard“, „Fokus“, „Bewegung des Mauszeigers“ und „Aktiv“. Um die Benutzeroberfläche für jeden dieser Status zu ändern, müssen wir den folgenden Pseudoklassen :hover
, :focus
und :active
Stile zuweisen, wie unten dargestellt:
.btn {
background-color: #4285f4;
}
.btn:hover {
background-color: #296cdb;
}
.btn:focus {
background-color: #0f52c1;
/* The outline parameter suppresses the border
color / outline when focused */
outline: 0;
}
.btn:active {
background-color: #0039a8;
}
In den meisten mobilen Browsern werden die Status Hover und/oder Focus auf ein Element angewendet, nachdem es angetippt wurde.
Überlegen Sie sorgfältig, welche Stile Sie festlegen und wie sie für den Nutzer aussehen werden, nachdem er seine Eingaben abgeschlossen hat.
Standardbrowserstile unterdrücken
Nachdem Sie Stile für die verschiedenen Status hinzugefügt haben, werden Sie feststellen, dass die meisten Browser ihre eigenen Stile als Reaktion auf die Berührung eines Nutzers implementieren. Das liegt vor allem daran, dass bei der Einführung von Mobilgeräten einige Websites keinen Stil für den Status :active
hatten. Daher haben viele Browser eine zusätzliche Hervorhebungsfarbe oder einen zusätzlichen Stil hinzugefügt, um den Nutzern Feedback zu geben.
In den meisten Browsern wird die CSS-Eigenschaft outline
verwendet, um einen Ring um ein Element anzuzeigen, wenn es den Fokus hat. Sie können sie mit folgenden Methoden unterdrücken:
.btn:focus {
outline: 0;
/* Add replacement focus styling here (i.e. border) */
}
Safari und Chrome fügen eine Farbe für die Hervorhebung von Tippaktionen hinzu, die mit der CSS-Eigenschaft -webkit-tap-highlight-color
verhindert werden kann:
/* Webkit / Chrome Specific CSS to remove tap
highlight color */
.btn {
-webkit-tap-highlight-color: transparent;
}
Internet Explorer auf Windows Phones funktioniert ähnlich, wird jedoch durch ein Meta-Tag unterdrückt:
<meta name="msapplication-tap-highlight" content="no">
Firefox hat zwei Nebenwirkungen.
Die Pseudoklasse -moz-focus-inner
, mit der berührbaren Elemente eine Umrisslinie hinzugefügt wird, können Sie entfernen, indem Sie border: 0
festlegen.
Wenn du in Firefox ein <button>
-Element verwendest, wird ein Farbverlauf angewendet, den du durch Festlegen von background-image: none
entfernen kannst.
/* Firefox Specific CSS to remove button
differences and focus ring */
.btn {
background-image: none;
}
.btn::-moz-focus-inner {
border: 0;
}
Nutzerauswahl deaktivieren
Beim Erstellen Ihrer UI kann es Szenarien geben, in denen Nutzer mit Ihren Elementen interagieren sollen, Sie aber das Standardverhalten beim Auswählen von Text unterdrücken möchten, wenn sie lange drücken oder die Maus über die Benutzeroberfläche ziehen.
Sie können dies mit der CSS-Eigenschaft user-select
tun. Beachten Sie jedoch, dass dies für Nutzer sehr ärgerlich sein kann, wenn sie den Text im Element auswählen möchten.
Verwenden Sie sie daher mit Vorsicht und sparsam.
/* Example: Disable selecting text on a paragraph element: */
p.disable-text-selection {
user-select: none;
}
Benutzerdefinierte Touch-Gesten implementieren
Wenn Sie eine Idee für benutzerdefinierte Interaktionen und Touch-Gesten für Ihre Website haben, sollten Sie zwei Dinge beachten:
- Alle Browser unterstützen
- So halten Sie die Framerate hoch.
In diesem Artikel gehen wir genau auf diese Themen ein. Wir behandeln die APIs, die wir unterstützen müssen, um alle Browser zu erreichen, und dann, wie wir diese Ereignisse effizient nutzen.
Je nachdem, was die Geste bewirken soll, möchten Sie wahrscheinlich, dass Nutzer jeweils nur mit einem Element interagieren oder dass sie mit mehreren Elementen gleichzeitig interagieren können.
In diesem Artikel sehen wir uns zwei Beispiele an, die die Unterstützung aller Browser und die Möglichkeit zeigen, die Framerate hoch zu halten.
Im ersten Beispiel kann der Nutzer mit einem Element interagieren. In diesem Fall möchten Sie vielleicht, dass alle Touch-Ereignisse diesem Element zugewiesen werden, sofern die Geste anfangs auf dem Element selbst begonnen hat. So kann beispielsweise ein Element auch dann gesteuert werden, wenn der Finger nicht mehr darauf liegt.
Das ist nützlich, da es den Nutzern viel Flexibilität bietet, aber die Interaktion der Nutzer mit der Benutzeroberfläche einschränkt.
Wenn Sie jedoch davon ausgehen, dass Nutzer gleichzeitig mit mehreren Elementen interagieren (mit Multitouch), sollten Sie die Touchbedienung auf das jeweilige Element beschränken.
Das ist für Nutzer flexibler, erschwert aber die Logik für die Manipulation der Benutzeroberfläche und ist weniger robust gegenüber Nutzerfehlern.
Event-Listener hinzufügen
In Chrome (Version 55 und höher), Internet Explorer und Edge wird für die Implementierung benutzerdefinierter Touch-Gesten die Verwendung von PointerEvents
empfohlen.
Für andere Browser sind TouchEvents
und MouseEvents
die richtige Vorgehensweise.
Das tolle Merkmal von PointerEvents
besteht darin, dass mehrere Arten von Eingaben, einschließlich Maus-, Berührungs- und Stiftereignisse, in einem Satz von Callbacks zusammengeführt werden. Die zu beobachtenden Ereignisse sind pointerdown
, pointermove
, pointerup
und pointercancel
.
Die entsprechenden Touch-Ereignisse in anderen Browsern sind touchstart
, touchmove
, touchend
und touchcancel
. Wenn Sie dieselbe Geste für die Mauseingabe implementieren möchten, müssen Sie mousedown
, mousemove
und mouseup
implementieren.
Weitere Informationen dazu, welche Ereignisse Sie verwenden sollten, finden Sie in der Tabelle Berührungs-, Maus- und Zeigerereignisse.
Wenn Sie diese Ereignisse verwenden möchten, müssen Sie die Methode addEventListener()
auf einem DOM-Element mit dem Namen eines Ereignisses, einer Rückruffunktion und einem booleschen Wert aufrufen.
Der boolesche Wert gibt an, ob das Ereignis vor oder nach anderen Elementen erfasst werden soll, die die Möglichkeit hatten, die Ereignisse zu erfassen und zu interpretieren. (true
bedeutet, dass das Ereignis vor anderen Elementen erscheinen soll.)
Hier ist ein Beispiel dafür, wie Sie auf den Beginn einer Interaktion warten.
// Check if pointer events are supported.
if (window.PointerEvent) {
// Add Pointer Event Listener
swipeFrontElement.addEventListener('pointerdown', this.handleGestureStart, true);
swipeFrontElement.addEventListener('pointermove', this.handleGestureMove, true);
swipeFrontElement.addEventListener('pointerup', this.handleGestureEnd, true);
swipeFrontElement.addEventListener('pointercancel', this.handleGestureEnd, true);
} else {
// Add Touch Listener
swipeFrontElement.addEventListener('touchstart', this.handleGestureStart, true);
swipeFrontElement.addEventListener('touchmove', this.handleGestureMove, true);
swipeFrontElement.addEventListener('touchend', this.handleGestureEnd, true);
swipeFrontElement.addEventListener('touchcancel', this.handleGestureEnd, true);
// Add Mouse Listener
swipeFrontElement.addEventListener('mousedown', this.handleGestureStart, true);
}
Interaktion mit einem einzelnen Element verarbeiten
Im kurzen Code-Snippet oben haben wir nur den Start-Ereignis-Listener für Mausereignisse hinzugefügt. Der Grund dafür ist, dass Mausereignisse nur ausgelöst werden, wenn der Cursor auf dem Element schwebt, dem der Ereignis-Listener hinzugefügt wurde.
TouchEvents
verfolgt eine Geste, nachdem sie gestartet wurde, unabhängig davon, wo die Berührung erfolgt. PointerEvents
verfolgt Ereignisse, unabhängig davon, wo die Berührung erfolgt, nachdem setPointerCapture
für ein DOM-Element aufgerufen wurde.
Für die Mausbewegungs- und End-Ereignisse fügen wir die Ereignis-Listener in der Geste-Startmethode und dem Dokument hinzu. So kann der Cursor bis zum Abschluss der Geste verfolgt werden.
Zur Implementierung sind folgende Schritte erforderlich:
- Fügen Sie alle TouchEvent- und PointerEvent-Listener hinzu. Fügen Sie für MouseEvents nur das Startereignis hinzu.
- Binden Sie im Rückruf für die Startgeste die Mausbewegungs- und Endereignisse an das Dokument. So werden alle Mausereignisse empfangen, unabhängig davon, ob das Ereignis auf dem ursprünglichen Element auftritt oder nicht. Für PointerEvents müssen wir
setPointerCapture()
auf unserem ursprünglichen Element aufrufen, um alle weiteren Ereignisse zu erhalten. Behandle dann den Beginn der Geste. - Behandle die Bewegungsereignisse.
- Entfernen Sie beim End-Ereignis die Mausbewegungs- und End-Listener aus dem Dokument und beenden Sie die Geste.
Unten sehen Sie ein Snippet unserer handleGestureStart()
-Methode, mit der dem Dokument die Ereignisse „move“ und „end“ hinzugefügt werden:
// Handle the start of gestures
this.handleGestureStart = function(evt) {
evt.preventDefault();
if(evt.touches && evt.touches.length > 1) {
return;
}
// Add the move and end listeners
if (window.PointerEvent) {
evt.target.setPointerCapture(evt.pointerId);
} else {
// Add Mouse Listeners
document.addEventListener('mousemove', this.handleGestureMove, true);
document.addEventListener('mouseup', this.handleGestureEnd, true);
}
initialTouchPos = getGesturePointFromEvent(evt);
swipeFrontElement.style.transition = 'initial';
}.bind(this);
Der End-Callback, den wir hinzufügen, lautet handleGestureEnd()
, wodurch die Ereignis-Listener „move“ und „end“ aus dem Dokument entfernt und die Zeigererfassung freigegeben wird, wenn die Geste beendet ist:
// Handle end gestures
this.handleGestureEnd = function(evt) {
evt.preventDefault();
if (evt.touches && evt.touches.length > 0) {
return;
}
rafPending = false;
// Remove Event Listeners
if (window.PointerEvent) {
evt.target.releasePointerCapture(evt.pointerId);
} else {
// Remove Mouse Listeners
document.removeEventListener('mousemove', this.handleGestureMove, true);
document.removeEventListener('mouseup', this.handleGestureEnd, true);
}
updateSwipeRestPosition();
initialTouchPos = null;
}.bind(this);
Wenn der Nutzer mit der Interaktion mit einem Element beginnt und seine Geste aus dem Element heraus verschiebt, erhalten wir weiterhin Mausbewegungen, unabhängig davon, wo sie sich auf der Seite befinden, da die Ereignisse vom Dokument empfangen werden.
In diesem Diagramm wird dargestellt, was die Touch-Ereignisse tun, wenn wir dem Dokument die Ereignisse „move“ und „end“ hinzufügen, sobald eine Geste beginnt.
Effizient auf Berührungen reagieren
Nachdem wir die Start- und Endereignisse eingerichtet haben, können wir jetzt auf die Touch-Ereignisse reagieren.
Bei allen Start- und Bewegungsereignissen können Sie x
und y
ganz einfach aus einem Ereignis extrahieren.
Im folgenden Beispiel wird geprüft, ob das Ereignis von einem TouchEvent
stammt. Dazu wird geprüft, ob targetTouches
vorhanden ist. Ist das der Fall, werden clientX
und clientY
aus der ersten Berührung extrahiert.
Wenn das Ereignis ein PointerEvent
oder MouseEvent
ist, werden clientX
und clientY
direkt aus dem Ereignis selbst extrahiert.
function getGesturePointFromEvent(evt) {
var point = {};
if (evt.targetTouches) {
// Prefer Touch Events
point.x = evt.targetTouches[0].clientX;
point.y = evt.targetTouches[0].clientY;
} else {
// Either Mouse event or Pointer Event
point.x = evt.clientX;
point.y = evt.clientY;
}
return point;
}
Ein TouchEvent
enthält drei Listen mit Touch-Daten:
touches
: Liste aller aktuellen Touch-Ereignisse auf dem Bildschirm, unabhängig vom DOM-Element, auf dem sie sich befinden.targetTouches
: Liste der Touch-Ereignisse, die derzeit auf dem DOM-Element stattfinden, an das das Ereignis gebunden ist.changedTouches
: Liste der Touch-Aktionen, die sich geändert haben und zu diesem Ereignis geführt haben.
In den meisten Fällen bietet targetTouches
alles, was Sie brauchen und wünschen. Weitere Informationen zu diesen Listen finden Sie unter Berührungslisten.
requestAnimationFrame verwenden
Da die Ereignis-Callbacks im Hauptthread ausgelöst werden, möchten wir in den Callbacks für unsere Ereignisse so wenig Code wie möglich ausführen, um die Framerate hoch zu halten und Verzögerungen zu vermeiden.
Mit requestAnimationFrame()
haben wir die Möglichkeit, die Benutzeroberfläche zu aktualisieren, kurz bevor der Browser einen Frame zeichnen möchte. So können wir einige Aufgaben aus unseren Ereignis-Callbacks herausnehmen.
Wenn Sie mit requestAnimationFrame()
nicht vertraut sind, finden Sie hier weitere Informationen.
Bei einer typischen Implementierung werden die Koordinaten x
und y
aus den Start- und Move-Ereignissen gespeichert und im Move-Ereignis-Callback wird ein Animationsframe angefordert.
In unserer Demo speichern wir die ursprüngliche Touch-Position in handleGestureStart()
(initialTouchPos
suchen):
// Handle the start of gestures
this.handleGestureStart = function(evt) {
evt.preventDefault();
if (evt.touches && evt.touches.length > 1) {
return;
}
// Add the move and end listeners
if (window.PointerEvent) {
evt.target.setPointerCapture(evt.pointerId);
} else {
// Add Mouse Listeners
document.addEventListener('mousemove', this.handleGestureMove, true);
document.addEventListener('mouseup', this.handleGestureEnd, true);
}
initialTouchPos = getGesturePointFromEvent(evt);
swipeFrontElement.style.transition = 'initial';
}.bind(this);
Die Methode handleGestureMove()
speichert die Position des Ereignisses, bevor bei Bedarf ein Animationsframe angefordert wird. Dabei wird unsere onAnimFrame()
-Funktion als Rückruf übergeben:
this.handleGestureMove = function (evt) {
evt.preventDefault();
if (!initialTouchPos) {
return;
}
lastTouchPos = getGesturePointFromEvent(evt);
if (rafPending) {
return;
}
rafPending = true;
window.requestAnimFrame(onAnimFrame);
}.bind(this);
Der Wert onAnimFrame
ist eine Funktion, die beim Aufrufen die Benutzeroberfläche verändert, um sie zu verschieben. Durch Übergabe dieser Funktion an requestAnimationFrame()
wird der Browser angewiesen, sie kurz vor der Aktualisierung der Seite aufzurufen (d.h. Änderungen an der Seite zu übernehmen).
Im handleGestureMove()
-Callback prüfen wir zuerst, ob rafPending
„false“ ist. Das zeigt an, ob onAnimFrame()
seit dem letzten Verschiebungsereignis von requestAnimationFrame()
aufgerufen wurde. Das bedeutet, dass immer nur ein requestAnimationFrame()
zur Ausführung bereitsteht.
Wenn unser onAnimFrame()
-Callback ausgeführt wird, setzen wir die Transformation für alle Elemente, die wir verschieben möchten, bevor wir rafPending
in false
aktualisieren. So kann beim nächsten Touch-Ereignis ein neuer Animationsframe angefordert werden.
function onAnimFrame() {
if (!rafPending) {
return;
}
var differenceInX = initialTouchPos.x - lastTouchPos.x;
var newXTransform = (currentXPosition - differenceInX)+'px';
var transformStyle = 'translateX('+newXTransform+')';
swipeFrontElement.style.webkitTransform = transformStyle;
swipeFrontElement.style.MozTransform = transformStyle;
swipeFrontElement.style.msTransform = transformStyle;
swipeFrontElement.style.transform = transformStyle;
rafPending = false;
}
Gesten mit Touch-Aktionen steuern
Mit der CSS-Eigenschaft touch-action
lässt sich das standardmäßige Touchverhalten eines Elements festlegen. In unseren Beispielen verwenden wir touch-action: none
, um zu verhindern, dass der Browser etwas mit den Touch-Gesten der Nutzer tut. So können wir alle Touch-Ereignisse abfangen.
/* Pass all touches to javascript: */
button.custom-touch-logic {
touch-action: none;
}
Die Verwendung von touch-action: none
ist eine drastische Maßnahme, da dadurch das gesamte Standard-Browserverhalten verhindert wird. In vielen Fällen ist eine der folgenden Optionen die bessere Lösung.
Mit touch-action
können Sie von einem Browser implementierte Touch-Gesten deaktivieren.
IE10 und höher unterstützen beispielsweise das Zoomen durch Doppeltippen. Wenn Sie touch-action
auf manipulation
setzen, wird das standardmäßige Doppeltippen verhindert.
So können Sie eine Doppeltippen-Geste selbst implementieren.
Nachfolgend finden Sie eine Liste häufig verwendeter touch-action
-Werte:
Unterstützung älterer IE-Versionen
Wenn Sie IE10 unterstützen möchten, müssen Sie Versionen von PointerEvents
mit Anbieterpräfix verarbeiten.
Um zu prüfen, ob PointerEvents
unterstützt wird, suchen Sie normalerweise nach window.PointerEvent
, aber in IE10 würden Sie nach window.navigator.msPointerEnabled
suchen.
Die Ereignisnamen mit Anbieterpräfixen sind 'MSPointerDown'
, 'MSPointerUp'
und 'MSPointerMove'
.
Im folgenden Beispiel wird gezeigt, wie Sie die Unterstützung für die Ereignisse prüfen und die Ereignisnamen ändern.
var pointerDownName = 'pointerdown';
var pointerUpName = 'pointerup';
var pointerMoveName = 'pointermove';
if (window.navigator.msPointerEnabled) {
pointerDownName = 'MSPointerDown';
pointerUpName = 'MSPointerUp';
pointerMoveName = 'MSPointerMove';
}
// Simple way to check if some form of pointerevents is enabled or not
window.PointerEventsSupport = false;
if (window.PointerEvent || window.navigator.msPointerEnabled) {
window.PointerEventsSupport = true;
}
Weitere Informationen finden Sie in diesem Artikel von Microsoft.
Referenz
Pseudoklassen für Touch-Status
Die endgültige Referenz zu Touch-Ereignissen finden Sie hier: W3C Touch Events.
Touch-, Maus- und Zeiger-Ereignisse
Diese Ereignisse sind die Bausteine, mit denen Sie Ihrer App neue Touch-Gesten hinzufügen können:
Touch-Listen
Jedes Touch-Ereignis umfasst drei Listenattribute:
Unterstützung für den Aktivitätsstatus auf iOS-Geräten aktivieren
Leider wird der Status aktiv in Safari auf iOS nicht standardmäßig angewendet. Damit er funktioniert, müssen Sie dem Dokumentkörper oder jedem Element einen touchstart
-Ereignis-Listener hinzufügen.
Sie sollten dies hinter einem User-Agent-Test tun, damit es nur auf iOS-Geräten ausgeführt wird.
Wenn Sie dem Body ein Touch-Start-Attribut hinzufügen, hat das den Vorteil, dass es auf alle Elemente im DOM angewendet wird. Dies kann jedoch zu Leistungsproblemen beim Scrollen der Seite führen.
window.onload = function() {
if (/iP(hone|ad)/.test(window.navigator.userAgent)) {
document.body.addEventListener('touchstart', function() {}, false);
}
};
Alternativ können Sie die Touch-Start-Listener allen interaktiven Elementen auf der Seite hinzufügen, um einige der Leistungsprobleme zu beheben.
window.onload = function() {
if (/iP(hone|ad)/.test(window.navigator.userAgent)) {
var elements = document.querySelectorAll('button');
var emptyFunction = function() {};
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener('touchstart', emptyFunction, false);
}
}
};