Touchscreens sind auf immer mehr Geräten verfügbar, von Smartphones bis hin zu Desktop-Bildschirmen. Ihre App sollte intuitiv und ansprechend auf Berührungen reagieren.
Touchscreens sind auf immer mehr Geräten verfügbar, von Smartphones bis hin zu Desktop-Bildschirmen. Wenn Nutzer mit Ihrer UI interagieren, sollte Ihre App intuitiv auf Berührungen reagieren.
Auf Elementstatus reagieren
Haben Sie schon einmal ein Element auf einer Webseite berührt oder angeklickt und sich gefragt, ob die Website es tatsächlich erkannt hat?
Wenn Sie einfach die Farbe eines Elements ändern, wenn Nutzer Teile der Benutzeroberfläche berühren oder damit interagieren, können Sie sich sicher sein, dass Ihre Website funktioniert. Das beseitigt nicht nur die Frustration, sondern sorgt auch für ein schnelles und reaktionsschnelles Gefühl.
DOM-Elemente können einen der folgenden Status übernehmen: „Standard“, „Fokus“, „Hover“ und „Aktiv“. Um die UI für jeden dieser Status zu ändern, müssen wir Stile auf die folgenden Pseudoklassen :hover
, :focus
und :active
anwenden, wie unten gezeigt:
.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 hover und/oder hover auf ein Element angewendet, nachdem es angetippt wurde.
Überlege dir gut, welche Stile du festgelegt hast und wie sie für die Nutzer aussehen, nachdem sie fertig sind.
Standardbrowserstile unterdrücken
Wenn Sie Stile für die verschiedenen Status hinzufügen, werden Sie feststellen, dass die meisten Browser ihre eigenen Stile als Reaktion auf die Berührung des Nutzers implementieren. Das liegt vor allem daran, dass bei der Einführung von Mobilgeräten auf einigen Websites kein Stil für den Status :active
vorhanden war. Infolgedessen fügten viele Browser zusätzliche Hervorhebungsfarbe oder ‐stil hinzu, um dem Nutzer Feedback zu geben.
In den meisten Browsern wird die CSS-Eigenschaft outline
verwendet, um ein Element um einen Ring herum anzuzeigen, wenn ein Element fokussiert ist. Sie können es unterdrücken mit:
.btn:focus {
outline: 0;
/* Add replacement focus styling here (i.e. border) */
}
Safari und Chrome fügen eine Markierungsfarbe hinzu, die durch die 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 verhält sich ähnlich, wird aber durch ein Meta-Tag unterdrückt:
<meta name="msapplication-tap-highlight" content="no">
Firefox hat zwei Nebeneffekte.
Die Pseudoklasse -moz-focus-inner
, die einen Umriss zu antippbaren Elementen hinzufügt, können Sie entfernen, indem Sie border: 0
festlegen.
Wenn Sie ein <button>
-Element in Firefox verwenden, wird ein Farbverlauf angewendet, den Sie entfernen können, indem Sie background-image: none
festlegen.
/* Firefox Specific CSS to remove button
differences and focus ring */
.btn {
background-image: none;
}
.btn::-moz-focus-inner {
border: 0;
}
Nutzerauswahl wird deaktiviert
Beim Erstellen der UI kann es Szenarien geben, in denen Nutzer mit Ihren Elementen interagieren sollen, aber Sie das Standardverhalten der Auswahl von Text bei langem Drücken oder Ziehen der Maus über die Benutzeroberfläche unterdrücken möchten.
Sie können dies mit der CSS-Eigenschaft user-select
tun. Allerdings kann dies bei Inhalten extremely verärgert für Nutzer sein, wenn sie den Text im Element auswählen möchten.
Verwende sie daher mit Vorsicht und sparsam.
/* Example: Disable selecting text on a paragraph element: */
p.disable-text-selection {
user-select: none;
}
Benutzerdefinierte Gesten implementieren
Wenn du Ideen für benutzerdefinierte Interaktionen und Gesten auf deiner Website hast, solltest du zwei Dinge beachten:
- Informationen zur Unterstützung aller Browser
- So sorgst du für eine hohe Framerate
In diesem Artikel werden genau diese Themen zu den APIs behandelt, die für alle Browser unterstützt werden müssen. Anschließend wird erläutert, wie wir diese Ereignisse effizient nutzen.
Je nachdem, was Sie mit Ihrer Geste ausführen möchten, möchten Sie wahrscheinlich, dass der Nutzer mit einzelnen Elementen interagieren kann oder sie sollen in der Lage sein, mit mehreren Elementen gleichzeitig zu interagieren.
In diesem Artikel sehen wir uns zwei Beispiele an, die zeigen, dass alle Browser unterstützt werden und wie eine hohe Framerate gehalten werden kann.
Im ersten Beispiel können Nutzende mit einem Element interagieren. In diesem Fall möchten Sie vielleicht, dass alle Touch-Ereignisse diesem Element zugewiesen werden, solange die Touch-Geste anfangs auf dem Element selbst begonnen hat. Beispielsweise kann das Element auch dann gesteuert werden, wenn ein Finger vom wischbaren Element entfernt wird.
Das ist nützlich, da der Nutzer dadurch ein hohes Maß an Flexibilität bietet, aber gleichzeitig eine Einschränkung der Interaktion des Nutzers mit der UI erzwingt.
Wenn Nutzer jedoch davon ausgehen, dass sie mit mehreren Elementen gleichzeitig mit mehreren Elementen interagieren (mithilfe von Multi-Touch), sollten Sie die Berührung auf das jeweilige Element beschränken.
Dies ist für Nutzer flexibler, erschwert jedoch die Logik zum Bearbeiten der Benutzeroberfläche und ist weniger fehleranfällig.
Event-Listener hinzufügen
In Chrome (Version 55 und höher), Internet Explorer und Edge, sind PointerEvents
der empfohlene Ansatz für die Implementierung benutzerdefinierter Gesten.
In anderen Browsern sind TouchEvents
und MouseEvents
die richtige Vorgehensweise.
Das große Merkmal von PointerEvents
ist, dass es mehrere Eingabetypen, einschließlich Maus-, Touch- und Stift-Ereignisse, zu einem Satz von Callbacks zusammenführt. Die Ereignisse, auf die gewartet werden soll, sind pointerdown
, pointermove
, pointerup
und pointercancel
.
Die Entsprechungen in anderen Browsern sind touchstart
, touchmove
, touchend
und touchcancel
für Touch-Ereignisse. Wenn Sie die gleiche Geste für die Mauseingabe implementieren möchten, müssen Sie mousedown
, mousemove
und mouseup
implementieren.
Informationen zu den zu verwendenden Ereignissen finden Sie in der Tabelle Ereignisse bei Berührung, Maus und Zeiger.
Wenn Sie diese Ereignisse verwenden möchten, müssen Sie die Methode addEventListener()
für ein DOM-Element zusammen mit dem Namen eines Ereignisses, einer Callback-Funktion und einem booleschen Wert aufrufen.
Der boolesche Wert bestimmt, ob das Ereignis erfasst werden soll, bevor oder nachdem andere Elemente die Möglichkeit hatten, sie zu erfassen und zu interpretieren. (true
bedeutet, dass das Ereignis vor anderen Elementen erscheinen soll.)
Hier ist ein Beispiel für das Erfassen des Beginns einer Interaktion.
// 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-Event-Listener für Mausereignisse hinzugefügt. Der Grund dafür ist, dass Mausereignisse nur dann ausgelöst werden, wenn sich der Cursor über das Element bewegt, dem der Event-Listener hinzugefügt wurde.
TouchEvents
erfasst eine Geste nach dem Start unabhängig davon, wo die Berührung erfolgt, und PointerEvents
erfasst Ereignisse unabhängig davon, wo die Berührung nach dem Aufruf von setPointerCapture
für ein DOM-Element erfolgt.
Bei Mausbewegungs- und Endereignissen fügen wir die Ereignis-Listener in der Gesten-Startmethode und dem Dokument hinzu. Dadurch kann der Cursor bis zum Abschluss der Geste verfolgt werden.
Um dies zu implementieren, sind folgende Schritte erforderlich:
- Alle TouchEvent- und PointerEvent-Listener hinzufügen. Fügen Sie für MouseEvents nur das Startereignis hinzu.
- Binden Sie im Callback für die Startbewegung die Ereignisse „Mausbewegung“ und „Ende“ an das Dokument. So werden alle Mausereignisse empfangen, unabhängig davon, ob das Ereignis für das ursprüngliche Element eintritt oder nicht. Bei PointerEvents müssen wir
setPointerCapture()
für unser ursprüngliches Element aufrufen, um alle weiteren Ereignisse zu empfangen. Bewege dann den Start der Touch-Geste. - Verarbeiten Sie die Verschiebungsereignisse.
- Entfernen Sie beim Ereignis „end“ die Mausbewegung und die End-Listener aus dem Dokument und beenden Sie die Touch-Geste.
Unten siehst du ein Snippet der Methode handleGestureStart()
, mit der die Ereignisse „move“ und „end“ dem Dokument 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, ist handleGestureEnd()
. Dieser entfernt die Ereignis-Listener für das Verschiebungs- und Endergebnis aus dem Dokument und lässt die Zeigererfassung los, wenn die Geste abgeschlossen ist. Beispiel:
// 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 einem Element interagiert und seine Geste aus dem Element herausbewegt, werden weiterhin Mausbewegungen ausgeführt, unabhängig davon, wo sich die Nutzer auf der Seite befinden, da die Ereignisse vom Dokument empfangen werden.
Dieses Diagramm zeigt, was die Touch-Ereignisse ausführen, während wir die Ereignisse "move" und "end" dem Dokument hinzufügen, sobald eine Geste beginnt.
Effizientes Reagieren auf Berührung
Nachdem wir die Start- und Endereignisse kennen, können wir auf die Touch-Ereignisse reagieren.
Für alle Start- und Move-Ereignisse können Sie x
und y
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 dies der Fall, werden clientX
und clientY
bei der ersten Berührung extrahiert.
Wenn das Ereignis ein PointerEvent
oder MouseEvent
ist, werden clientX
und clientY
direkt aus dem Ereignis 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;
}
Eine TouchEvent
hat drei Listen mit Berührungsdaten:
touches
: Liste aller aktuellen Berührungen auf dem Bildschirm, unabhängig vom DOM-Element, auf dem sie sich befinden.targetTouches
: Liste der Berührungen des DOM-Elements, an das das Ereignis gebunden ist.changedTouches
: Liste der Berührungen, die sich geändert haben, was zum Auslösen des Ereignisses geführt hat.
In den meisten Fällen bietet targetTouches
alles, was Sie brauchen und was Sie brauchen. Weitere Informationen zu diesen Listen finden Sie unter Touchlisten.
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. So bleibt unsere Framerate hoch und verhindert Verzögerungen.
Mit requestAnimationFrame()
können wir die Benutzeroberfläche aktualisieren, kurz bevor der Browser einen Frame zeichnen möchte. Dadurch können wir einige Vorgänge aus unseren Ereignis-Callbacks entfernen.
Wenn Sie mit requestAnimationFrame()
nicht vertraut sind, finden Sie hier weitere Informationen.
Eine typische Implementierung besteht darin, die x
- und y
-Koordinaten des Startereignisses zu speichern und die Ereignisse zu verschieben und einen Animationsframe innerhalb des Callbacks für das Verschiebungsereignis anzufordern.
In unserer Demo speichern wir die anfängliche Berührungsposition in handleGestureStart()
(suchen Sie nach initialTouchPos
):
// 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 die Funktion onAnimFrame()
als Callback ü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 UI ändert, um sie zu verschieben. Durch die Übergabe dieser Funktion an requestAnimationFrame()
bitten wir den Browser, sie kurz vor der Aktualisierung der Seite aufzurufen (d.h. alle Änderungen an der Seite zu übertragen).
Im handleGestureMove()
-Callback prüfen wir zuerst, ob rafPending
„false“ ist. Das bedeutet, dass onAnimFrame()
seit dem letzten Verschiebungsereignis von requestAnimationFrame()
aufgerufen wurde. Es muss also immer nur ein requestAnimationFrame()
-Objekt auf einmal ausgeführt werden.
Wenn der onAnimFrame()
-Callback ausgeführt wird, legen wir die Transformation für alle Elemente fest, die verschoben werden sollen, bevor wir rafPending
auf false
aktualisieren. So kann das nächste Touch-Ereignis einen neuen Animationsframe anfordern.
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;
}
Touch-Gesten mithilfe von Touch-Aktionen steuern
Mit der CSS-Eigenschaft touch-action
können Sie das standardmäßige Berührungsverhalten eines Elements steuern. In unseren Beispielen verwenden wir touch-action: none
, um zu verhindern, dass der Browser etwas mit der Berührung eines Nutzers 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 eher unwichtig, da damit das gesamte Standardverhalten des Browsers 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.
In Internet Explorer 10 und höher wird beispielsweise das Zoomen durch zweimaliges Tippen unterstützt. Wenn Sie für touch-action
den Wert manipulation
festlegen, verhindern Sie das standardmäßige Verhalten beim Doppeltippen.
So können Sie selbst eine Touch-Geste für das Doppeltippen implementieren.
Nachfolgend finden Sie eine Liste häufig verwendeter touch-action
-Werte:
Unterstützung älterer Versionen von IE
Wenn IE10 unterstützt werden soll, müssen Sie Versionen mit Anbieterpräfixen von PointerEvents
verarbeiten.
Um zu prüfen, ob PointerEvents
unterstützt wird, suchen Sie normalerweise nach window.PointerEvent
, in Internet Explorer 10 hingegen nach window.navigator.msPointerEnabled
.
Die Ereignisnamen mit Anbieterpräfixen sind 'MSPointerDown'
, 'MSPointerUp'
und 'MSPointerMove'
.
Das folgende Beispiel zeigt, wie Sie prüfen, ob Support unterstützt wird, 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 zu Microsoft-Updates.
Referenz
Pseudoklassen für Touch-Zustände
Die maßgebliche Referenz zu Touch-Ereignissen finden Sie hier: W3C-Touch-Ereignisse.
Touch-, Maus- und Zeigerereignisse
Diese Ereignisse sind die Bausteine, um Ihrer App neue Touch-Gesten hinzuzufügen:
Berührungslisten
Jedes Berührungsereignis umfasst drei Listenattribute:
Unterstützung für den aktiven Status unter iOS aktivieren
Leider wird in Safari unter iOS der Status active nicht standardmäßig angewendet. Damit alles funktioniert, müssen Sie dem Dokumenttext oder jedem Element einen touchstart
-Event-Listener hinzufügen.
Dies sollte hinter einem User-Agent-Test erfolgen, damit der Test nur auf iOS-Geräten funktioniert.
Das Hinzufügen einer Berührungsstartseite in den Text hat den Vorteil, dass sie auf alle Elemente im DOM angewendet wird. Dies kann jedoch beim Scrollen auf der Seite zu Leistungsproblemen führen.
window.onload = function() {
if (/iP(hone|ad)/.test(window.navigator.userAgent)) {
document.body.addEventListener('touchstart', function() {}, false);
}
};
Alternativ können Sie allen interaktiven Elementen auf der Seite die Touch-Start-Listener hinzufügen, um einige Leistungseinbußen zu verringern.
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);
}
}
};