הוספת מגע לאתר

מסכי מגע זמינים ביותר ויותר מכשירים, מטלפונים ועד למסכי מחשב. האפליקציה צריכה להגיב למגע שלהם בדרכים אינטואיטיביות ויפות.

מאט גאונט

מסכי מגע זמינים ביותר ויותר מכשירים, מטלפונים ועד למסכים של מחשבים שולחניים. כשהמשתמשים בוחרים לקיים אינטראקציה עם ממשק המשתמש, האפליקציה צריכה להגיב למגע שלהם בדרכים אינטואיטיביות.

תגובה למצבי הרכיב

האם אי פעם נגעת או לחצת על אלמנט בדף אינטרנט ושאלת אם האתר מזהה אותו בפועל?

שינוי הצבע של אלמנט בזמן שהמשתמשים נוגעים בחלקים בממשק המשתמש או יוצרים איתם אינטראקציה, נותן ביטחון בסיסי שהאתר שלך פועל. מעבר לזה, מפחית תסכול, הוא גם יכול להעניק תחושה מהירה ורספונסיבית.

רכיבי DOM יכולים לקבל בירושה כל אחד מהמצבים הבאים: ברירת מחדל, מיקוד, העברת עכבר ופעיל. כדי לשנות את ממשק המשתמש שלנו לכל אחד מהמצבים האלה, צריך להחיל סגנונות על פסאודו המחלקות הבאות: :hover, :focus ו-:active, כפי שמוצג כאן:

.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;
}

רוצים לנסות?

תמונה להמחשה של צבעים שונים
למצבי לחצנים

ברוב הדפדפנים בנייד, מצבי hover ו/או hover יחולו על רכיב מסוים אחרי שמקישים עליו.

מומלץ לחשוב היטב איזה סגנונות להגדיר ואיך הם ייראו למשתמש אחרי שהוא יסתיים.

התעלמות מסגנונות ברירת המחדל של הדפדפן

אחרי שמוסיפים סגנונות למצבים השונים, תבחינו שרוב הדפדפנים מטמיעים סגנונות משלהם בהתאם למגע של המשתמש. הסיבה העיקרית לכך היא כשמכשירים ניידים הושקו לראשונה, לחלק מהאתרים לא היה עיצוב למצב :active. כתוצאה מכך, דפדפנים רבים הוסיפו עוד צבע או סגנון להדגשה על מנת לתת למשתמש משוב.

רוב הדפדפנים משתמשים במאפיין ה-CSS outline כדי להציג טבעת מסביב לרכיב כשהרכיב מתמקד בו. אפשר להסתיר אותו באמצעות:

.btn:focus {
    outline: 0;

    /* Add replacement focus styling here (i.e. border) */
}

ב-Safari וב-Chrome מוסיפים צבע להדגשה בהקשה, שניתן למנוע באמצעות מאפיין ה-CSS -webkit-tap-highlight-color:

/* Webkit / Chrome Specific CSS to remove tap
highlight color */
.btn {
  -webkit-tap-highlight-color: transparent;
}

רוצים לנסות?

ל-Internet Explorer ב-Windows Phone יש התנהגות דומה, אבל הוא מבוטל באמצעות מטא תג:

<meta name="msapplication-tap-highlight" content="no">

ל-Firefox יש שתי תופעות לוואי לטיפול.

פסאודו המחלקה -moz-focus-inner, שמוסיפה קו מתאר לאלמנטים שניתן לגעת בהם, אפשר להסיר אותם על ידי הגדרת border: 0.

אם משתמשים באלמנט <button> ב-Firefox, מיושמת הדרגתית, ואפשר להסיר אותה על ידי הגדרת background-image: none.

/* Firefox Specific CSS to remove button
differences and focus ring */
.btn {
  background-image: none;
}

.btn::-moz-focus-inner {
  border: 0;
}

רוצים לנסות?

השבתת 'בחירת משתמשים'

כשיוצרים ממשק משתמש, יכולים להיות תרחישים שבהם תרצו שהמשתמשים ייצרו אינטראקציה עם הרכיבים, אבל תרצו לבטל את התנהגות ברירת המחדל של בחירת טקסט בלחיצה ארוכה או גרירת העכבר מעל לממשק המשתמש.

אפשר לעשות זאת באמצעות מאפיין ה-CSS user-select, אבל חשוב לשים לב שביצוע פעולה זו בתוכן עלול להכעיס extremely את המשתמשים אם הם רוצים לבחור את הטקסט ברכיב. לכן צריך להשתמש בו בזהירות ובצמצום.

/* Example: Disable selecting text on a paragraph element: */
p.disable-text-selection {
  user-select: none;
}

הטמעת תנועות מותאמות אישית

אם יש לכם רעיון לאינטראקציות ולתנועות מותאמות אישית באתר, יש שני נושאים שכדאי לזכור:

  1. הנחיות לתמיכה בכל הדפדפנים.
  2. איך לשמור על קצב פריימים גבוה.

במאמר הזה נציג בדיוק את הנושאים האלה, שעוסקים ב-API שבו אנחנו צריכים לתמוך על מנת להגיע לכל הדפדפנים, ואז נסביר איך אנחנו משתמשים באירועים האלה ביעילות.

בהתאם למה שאתם רוצים שהתנועה תבצע, סביר להניח שתרצו שהמשתמש יבצע אינטראקציה עם רכיב אחד בכל פעם או שתרצו שהוא יוכל לקיים אינטראקציה עם מספר רכיבים בו-זמנית.

נבחן שתי דוגמאות במאמר זה – כדי להדגים תמיכה בכל הדפדפנים ואיך לשמור על קצב פריימים גבוה.

דוגמה ל-GIF של נגיעה במסמך

הדוגמה הראשונה תאפשר למשתמש לבצע אינטראקציה עם רכיב אחד. במקרה כזה, יכול להיות שתרצו שכל אירועי המגע יוענקו לאלמנט הזה, כל עוד התנועה התחילה בהתחלה על האלמנט עצמו. לדוגמה, גם אם תזיזו את האצבע מהרכיב שאפשר להחליק, עדיין תוכלו לשלוט בו.

האפשרות הזו שימושית כי היא מאפשרת למשתמשים הרבה גמישות, אבל מגבילה את היכולת שלהם לקיים אינטראקציה עם ממשק המשתמש.

דוגמה לקובץ GIF של מגע על אלמנט

לעומת זאת, אם אתם מצפים שמשתמשים יוכלו לקיים אינטראקציה עם מספר רכיבים בו-זמנית (באמצעות ריבוי נקודות מגע), עליכם להגביל את המגע לרכיב הספציפי.

זו שיטה גמישה יותר למשתמשים, אבל היא מסבכת את הלוגיקה למניפולציה של ממשק המשתמש ופחות עמידה בפני שגיאות משתמשים.

הוספת פונקציות event listener

ב-Chrome (מגרסה 55 ואילך), Internet Explorer ו-Edge, PointerEvents היא הגישה המומלצת להטמעת תנועות מותאמות אישית.

בדפדפנים אחרים, TouchEvents ו-MouseEvents הם הגישה הנכונה.

היתרון הנהדר של PointerEvents הוא שהוא ממזג כמה סוגי קלט, כולל אירועי עכבר, מגע ועט, לקבוצה אחת של קריאה חוזרת. האירועים שצריך להאזין להם הם pointerdown, pointermove, pointerup ו-pointercancel.

האפליקציות המקבילות בדפדפנים אחרים הן touchstart, touchmove, touchend ו-touchcancel לאירועי מגע. אם רוצים להטמיע את אותה תנועה לקלט עכבר, צריך להטמיע את mousedown, את mousemove ואת mouseup.

אם יש לכם שאלות לגבי האירועים שבהם כדאי להשתמש, תוכלו להיעזר בטבלה הבאה אירועי מגע, עכבר ומצביע.

כדי להשתמש באירועים האלה צריך לקרוא לשיטה addEventListener() ברכיב DOM, לצד שם האירוע, פונקציית קריאה חוזרת ופרמטר בוליאני. הערך הבוליאני קובע אם צריך לתפוס את האירוע לפני או אחרי שאלמנטים אחרים קיבלו הזדמנות לקלוט ולפרש את האירועים. (המשמעות של true היא שהאירוע לפני רכיבים אחרים).

הנה דוגמה להאזנה להתחלת אינטראקציה.

// 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);
}

רוצים לנסות?

טיפול באינטראקציה של רכיב יחיד

בקטע הקוד הקצר שלמעלה, הוספנו רק את ה-event listener הפותח לאירועי עכבר. הסיבה לכך היא שאירועי העכבר יוקפצו רק כשמעבירים את העכבר מעל לאלמנט שאליו מתווסף event listener.

TouchEvents יעקוב אחרי תנועה אחרי שהיא מתחילה, בלי קשר למקום שבו התרחש הנגיעה, ו-PointerEvents יעקוב אחרי אירועים בלי קשר למקום שבו התרחש הנגיעה אחרי קריאה ל-setPointerCapture ברכיב DOM.

עבור אירועי העברת עכבר וסיום, אנחנו מוסיפים את פונקציות event listener בשיטת ההתחלה של התנועות ומוסיפים את ה-listener למסמך, כלומר הוא יכול לעקוב אחרי הסמן עד להשלמת התנועה.

כדי ליישם זאת:

  1. הוספת כל המאזינים של TouchEvent ו-PointerEvent. ב-MouseEvents, מוסיפים רק את אירוע ההתחלה.
  2. בתוך הקריאה החוזרת (callback) של תנועת ההתחלה, מקשרים את תנועת העכבר ומסיימים אירועים למסמך. כך כל אירועי העכבר מתקבלים, גם אם האירוע התרחש ברכיב המקורי וגם אם לא. עבור PointerEvents, אנחנו צריכים להפעיל את setPointerCapture() ברכיב המקורי שלנו כדי לקבל את כל האירועים הנוספים. לאחר מכן יש לטפל בהתחלת התנועה.
  3. טיפול באירועי ההעברה.
  4. באירוע הסיום, יש להסיר את העברת העכבר, לסיים את ה-listener מהמסמך ולסיים את התנועה.

בהמשך מופיע קטע של השיטה handleGestureStart() שמוסיפה למסמך את אירועי ההעברה והסיום:

// 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);

רוצים לנסות?

הקריאה החוזרת (callback) שנוספת היא handleGestureEnd(), שמסירה את ה-event listener להעברה ולסיום מהמסמך ומשחררת את תיעוד הסמן כשהתנועה מסתיימת כך:

// 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);

רוצים לנסות?

בהתאם לדפוס הזה של הוספת אירוע ההעברה למסמך, אם משתמש מתחיל אינטראקציה עם רכיב ומזיז את התנועה שלו אל מחוץ לאלמנט, נמשיך לקבל תנועות עכבר ללא קשר למיקום של הדף, מכיוון שהאירועים מתקבלים מהמסמך.

בתרשים הזה רואים מה עושים אירועי המגע בזמן שאנחנו מוסיפים את אירועי ההעברה והסיום למסמך ברגע שמתחילה תנועה.

איור של אירועי מגע מקשרים למסמך ב-&#39;Touchstart&#39;

תגובה יעילה למגע

עכשיו, אחרי שטיפלנו באירועי ההתחלה והסיום, אנחנו יכולים להגיב לאירועי המגע.

לכל אירוע התחלה והעברה, אפשר לחלץ בקלות את x ואת y מאירוע.

הדוגמה הבאה מוודאת שהאירוע הוא מ-TouchEvent על ידי בדיקה אם האירוע targetTouches קיים. אם כן, הוא מחלץ את clientX ו-clientY מהנגיעה הראשונה. אם האירוע הוא PointerEvent או MouseEvent, הוא מחלץ את clientX ואת clientY ישירות מהאירוע עצמו.

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;
  }

רוצים לנסות?

ל-TouchEvent יש שלוש רשימות שמכילות נתוני מגע:

  • touches: רשימה של כל הנגיעות הנוכחיות במסך, בלי קשר לרכיב ה-DOM שבו.
  • targetTouches: רשימת נגיעות ברכיב ה-DOM שאליו האירוע קשור.
  • changedTouches: רשימת נגיעות שהשתנו והובילו להפעלת האירוע.

ברוב המקרים, ב-targetTouches יש את כל מה שצריך. (למידע נוסף על הרשימות האלה, ראו רשימות מגע).

שימוש ב-requestAnimationFrame

הקריאות החוזרות של האירועים מופעלות ב-thread הראשי, ולכן אנחנו רוצים להריץ בקריאות החוזרות (callback) כמה שיותר קוד – כדי לשמור על קצב פריימים גבוה ולמנוע בעיות בממשק (jank).

באמצעות requestAnimationFrame() יש לנו הזדמנות לעדכן את ממשק המשתמש ממש לפני שהדפדפן מתכוון לשרטט מסגרת, והוא יעזור לנו להוציא חלק מהקריאות החוזרות של האירועים.

אם אתם לא מכירים את requestAnimationFrame(), מידע נוסף זמין כאן.

הטמעה אופיינית היא שמירת הקואורדינטות x ו-y מאירועי ההתחלה וההעברה, ולבקש מסגרת אנימציה בתוך הקריאה החוזרת לאירוע ההעברה.

בהדגמה שלנו, אנחנו מאחסנים את מיקום המגע הראשוני ב-handleGestureStart() (חיפוש 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);

השיטה handleGestureMove() שומרת את מיקום האירוע לפני שמבקשים פריים אנימציה, במקרה הצורך, ומעבירה את הפונקציה onAnimFrame() כקריאה חוזרת:

this.handleGestureMove = function (evt) {
  evt.preventDefault();

  if (!initialTouchPos) {
    return;
  }

  lastTouchPos = getGesturePointFromEvent(evt);

  if (rafPending) {
    return;
  }

  rafPending = true;

  window.requestAnimFrame(onAnimFrame);
}.bind(this);

הערך onAnimFrame הוא פונקציה שכאשר מפעילים אותה, היא משנה את ממשק המשתמש שלנו כדי להזיז אותו. על ידי העברת הפונקציה הזו אל requestAnimationFrame(), אנחנו מנחים את הדפדפן לקרוא לפונקציה ממש לפני שהוא עומד לעדכן את הדף (כלומר, לצבוע שינויים בדף).

בקריאה החוזרת (callback) של handleGestureMove() אנחנו בודקים בהתחלה אם rafPending מוגדר כ-False, שמציין אם בוצעה קריאה ל-onAnimFrame() על ידי requestAnimationFrame() מאז אירוע ההעברה האחרון. כלומר, בכל רגע נתון יש לנו רק requestAnimationFrame() אחד שממתין להפעלה.

כשמתבצעת קריאה חוזרת (callback) של onAnimFrame(), אנחנו מגדירים את הטרנספורמציה בכל הרכיבים שאנחנו רוצים להעביר לפני שמעדכנים את rafPending ל-false, כדי לאפשר לאירוע המגע הבא לבקש מסגרת אנימציה חדשה.

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;
}

שליטה בתנועות באמצעות פעולות מגע

מאפיין ה-CSS touch-action מאפשר לשלוט בהתנהגות המגע המוגדרת כברירת מחדל ברכיב. בדוגמאות שלנו אנחנו משתמשים ב-touch-action: none כדי למנוע מהדפדפן לעשות כל פעולה במגע של משתמשים, וכך ליירט את כל אירועי המגע.

/* Pass all touches to javascript: */
button.custom-touch-logic {
  touch-action: none;
}

השימוש ב-touch-action: none הוא אפשרות גרעינית במידה מסוימת, כי הוא מונע את כל התנהגויות ברירת המחדל של הדפדפן. במקרים רבים, אחת מהאפשרויות שבהמשך היא פתרון טוב יותר.

באמצעות touch-action אפשר להשבית תנועות שהוטמעו בדפדפן. לדוגמה, IE10+ תומך בתנועת הקשה כפולה לשינוי מרחק התצוגה. הגדרה של touch-action של manipulation מונעת את התנהגות ברירת המחדל של הקשה כפולה.

כך תהיה לך אפשרות להטמיע תנועה של הקשה כפולה בעצמך.

בהמשך מופיעה רשימה של ערכי touch-action נפוצים:

פרמטרים של פעולות מגע
touch-action: none הדפדפן לא יטפל באינטראקציות במגע.
touch-action: pinch-zoom משביתה את כל האינטראקציות של הדפדפן, כמו 'touch-action: none' מלבד 'צביטה מרחק מתצוגה', שהדפדפן עדיין מטופל.
touch-action: pan-y pinch-zoom אפשר לטפל בגלילות אופקיות ב-JavaScript בלי להשבית גלילה אנכית או שינוי מרחק התצוגה (למשל, קרוסלות תמונות).
touch-action: manipulation משבית את תנועת ההקשה הכפולה שמונעת השהיה של לחיצה על ידי הדפדפן. משאיר את הגלילה ומתמקמת בתנועת צביטה כלפי מעלה בדפדפן.

תמיכה בגרסאות ישנות יותר של IE

אם רוצים לתמוך ב-IE10, צריך לטפל בגרסאות עם קידומת של הספק של PointerEvents.

בדרך כלל, מומלץ לחפש window.PointerEvent על מנת לבדוק אם יש תמיכה ב-PointerEvents, אבל ב-IE10 עדיף לחפש window.navigator.msPointerEnabled.

שמות האירועים עם קידומות של ספקים הם: 'MSPointerDown', 'MSPointerUp' ו-'MSPointerMove'.

הדוגמה הבאה ממחישה איך לבדוק אם יש תמיכה ולשנות את שמות האירועים.

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;
}

למידע נוסף, קראו את המאמר הזה על עדכון מ-Microsoft.

חומרי עזר

מחלקות פסאודוגרפיות למצבי מגע

מחלקה דוגמה תיאור
:hover
הלחצן במצב לחוץ
הוזן כשסמן העכבר נמצא מעל רכיב. שינויים בממשק המשתמש בהעברת העכבר עוזרים לעודד את המשתמשים לבצע אינטראקציה עם רכיבים.
:Focus
לחצן עם מצב מיקוד
מוזנת כאשר המשתמש גולש בין רכיבים בדף. מצב המיקוד מאפשר למשתמשים לדעת עם איזה רכיב הוא נמצא כרגע באינטראקציה וגם מאפשר למשתמשים לנווט בקלות בממשק המשתמש באמצעות מקלדת.
:פעיל
הלחצן במצב לחוץ
הפרמטר הוזן בזמן בחירה ברכיב, לדוגמה, כשמשתמש לוחץ על רכיב או נוגע בו.

ההפניה הטובה ביותר לאירועי מגע נמצאת כאן: אירועי מגע של W3C.

אירועי מגע, עכבר וסמן

האירועים האלה הם אבני הבניין להוספת תנועות חדשות לאפליקציה:

אירועי מגע, עכבר, מצביע
touchstart, mousedown, pointerdown הפעולה הזו נקראת כאשר אצבע נוגעת בפעם הראשונה ברכיב או כשהמשתמש לוחץ על העכבר.
touchmove, mousemove, pointermove מתבצעת קריאה לפעולה כשהמשתמש מזיז את האצבע על פני המסך או גורר עם העכבר.
touchend, mouseup, pointerup מתבצעת קריאה לפעולה כאשר המשתמש מרים את האצבע מהמסך או משחרר את העכבר.
touchcancel pointercancel קוראים לזה כשהדפדפן מבטל את תנועות המגע. לדוגמה, משתמש ייגע באפליקציית אינטרנט ולאחר מכן עבר בין כרטיסיות.

רשימות מגע

כל אירוע מגע כולל שלושה מאפייני רשימה:

מאפיינים של אירועי מגע
touches רשימה של כל הנגיעות הנוכחיות במסך, ללא קשר לאלמנטים שנוגעים בהם.
targetTouches רשימת נגיעות שהתחילו ברכיב שהוא היעד של האירוע הנוכחי. לדוגמה, אם הכריכה מתבצעת אל <button>, יוצגו רק נגיעות בלחצן הזה בשלב הזה. אם מקשרים למסמך, נוגעים בכל המגעים הקיימים במסמך.
changedTouches רשימת נגיעות שהשתנו והובילו להפעלת האירוע:
  • עבור האירוע touchstart - רשימה של נקודות המגע שהפכו לפעילות עכשיו עם האירוע הנוכחי.
  • עבור האירוע touchmove - רשימה של נקודות המגע שזזו מאז האירוע האחרון.
  • עבור האירועים touchend ו- touchcancel - רשימה של נקודות המגע שהוסרו מהמשטח.

הפעלת תמיכה במצבים פעילים ב-iOS

לצערנו, Safari ב-iOS לא מחיל את המצב active כברירת מחדל. כדי שהוא יפעל, צריך להוסיף פונקציות event listener ל-touchstart לגוף המסמך או לכל רכיב.

צריך לבצע את הבדיקה באמצעות בדיקה של סוכן המשתמש, כך שהיא תפעל רק במכשירי iOS.

היתרון של הוספת התחלת מגע לגוף הוא החלה על כל הרכיבים ב-DOM, אבל עשויות להיות בעיות בביצועים בזמן הגלילה בדף.

window.onload = function() {
  if (/iP(hone|ad)/.test(window.navigator.userAgent)) {
    document.body.addEventListener('touchstart', function() {}, false);
  }
};

החלופה היא להוסיף את המאזינים להתחלת המגע לכל הרכיבים האינטראקטיביים בדף, וכך להפחית חלק מהבעיות בביצועים.

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);
    }
  }
};