16 липня 2023 р.

Події миші

У цьому розділі ми докладніше розглянемо події миші та їх властивості.

Зверніть увагу: подібні події можуть надходити не тільки від миші, але й з інших пристроїв, таких як телефони та планшети, де вони емулюються для сумісності.

Типи подій миші

Ми вже бачили деякі з цих подій:

mousedown/mouseup
Клікання/відпускання кнопки миші над елементом.
mouseover/mouseout
Курсор заходить/виходить з елемента.
mousemove
Кожне переміщення миші над елементом викликає цю подію.
click
Спрацьовує після mousedown, а потім mouseup над тим же елементом, якщо була використана ліва кнопка миші.
dblclick
Спрацьовує після двох кліків на одному елементі за короткий проміжок часу. На сьогоднішній день рідко використовується.
contextmenu
Спрацьовує при натисканні правої кнопки миші. Є й інші способи відкрити контекстне меню, напр. за допомогою спеціальної клавіші на клавіатурі, але це вже не зовсім подія миші.

…Також існують кілька інших подій, про них ми розповімо пізніше.

Порядок подій

Як ви можете бачити зі списку вище, дія користувача може викликати безліч подій.

Наприклад, клік лівою кнопкою спочатку ініціює mousedown, коли кнопку натиснуто, потім mouseup і click, коли її відпускають.

У випадках, коли одна дія ініціює декілька подій, їх порядок фіксується. Тобто обробники викликаються в порядку mousedownmouseupclick.

Клікніть кнопку нижче, і ви побачите події. Спробуйте також клікнути двічі.

Всі події миші реєструються у тестовому вікні нижче, і якщо між ними є затримка більше 1 секунди, вони розділяються горизонтальною лінією.

Також ми можемо побачити властивість button, яка дозволяє виявити яку саме кнопку миші клікнули, це пояснюється нижче.

Кнопка миші

Події, пов’язані з кліками, завжди мають властивість button, що дозволяє отримати точну кнопку миші.

Зазвичай ми не використовуємо її для подій click і contextmenu, оскільки перше відбувається лише при натисканні лівою кнопкою миші, а останнє – лише при натисканні правою кнопкою миші.

З іншого боку, обробникам mousedown та mouseup може знадобитися event.button, тому що ці події запускаються на будь-якій кнопці, тому button дозволяє розрізняти “праву кнопку миші” та “ліву кнопку миші”.

Можливі значення event.button:

Стан кнопки event.button
Ліва кнопка (основна) 0
Середня кнопка (допоміжна) 1
Права кнопка (другорядна) 2
Кнопка X1 (назад) 3
Кнопка X2 (вперед) 4

Більшість пристроїв мають лише ліву та праву кнопки, тому можливі значення 0 або 2. Сенсорні пристрої також генерують подібні події, коли на них натискають.

Також є властивість event.buttons, яка містить усі натиснуті кнопки як ціле число, по одному біту на кнопку. На практиці ця властивість використовується дуже рідко, деталі можна знайти на сторінці MDN, якщо вона вам колись знадобиться.

Застаріла властивість event.which

Старий код може використовувати властивість event.which, що є застарілим нестандартним способом отримання кнопки з можливими значеннями:

  • event.which == 1 – ліва кнопка,
  • event.which == 2 – середня кнопка,
  • event.which == 3 – права кнопка.

На даний момент властивість event.which застаріла, ми не повинні її використовувати.

Модифікатори: shift, alt, ctrl і meta

Усі події миші містять інформацію про натиснуті клавіші-модифікатори.

Властивості події:

  • shiftKey: Shift
  • altKey: Alt (чи Opt для Mac)
  • ctrlKey: Ctrl
  • metaKey: Cmd для Mac

Вони мають значення true, якщо відповідна клавіша була натиснута під час події.

Наприклад, кнопка нижче працює лише на Alt+Shift + клік:

<button id="button">Alt+Shift+Клікни мене!</button>

<script>
  button.onclick = function(event) {
    if (event.altKey && event.shiftKey) {
      alert('Ура!');
    }
  };
</script>
Увага: на Mac зазвичай Cmd замість Ctrl

На Windows і Linux є клавіші-модифікатори Alt, Shift і Ctrl. На Mac є ще одна: Cmd, що відповідає властивості metaKey.

У більшості застосунків, коли Windows/Linux використовує Ctrl, на Mac використовується Command.

Тобто: коли користувач Windows натискає Ctrl+Enter або Ctrl+A, користувач Mac натискає Cmd+Enter або Cmd+A, і так далі.

Отже, якщо ми хочемо підтримувати такі комбінації, як Ctrl+ клік, то для Mac має сенс використовувати Cmd+ клік. Це зручніше для користувачів Mac.

Навіть якщо ми б хотіли змусити користувачів Mac застосувати Ctrl+клік – це трохи складно. Проблема полягає в тому, що клік лівою кнопкою миші за допомогою Ctrl інтерпретується як клік правою кнопкою миші у MacOS, і він генерує подію contextmenu, а не click, як Windows/Linux.

Тож якщо ми хочемо, щоб користувачі всіх операційних систем відчували себе комфортно, то разом із ctrlKey ми повинні перевірити metaKey.

Для JS-коду це означає, що ми повинні перевірити if (event.ctrlKey || event.metaKey).

Є також мобільні пристрої

Комбінації клавіатури хороші як доповнення до робочого процесу. Так що якщо відвідувач використовує клавіатуру – вони працюють.

Але якщо в їхньому пристрої цього немає – то має бути спосіб жити без клавіш-модифікаторів.

Координати: clientX/Y, pageX/Y

Усі події миші надають координати у двох варіантах:

  1. Відносно Window: clientX та clientY.
  2. Відносно Document: pageX та pageY.

Ми вже розглянули різницю між ними у розділі Координати.

Коротше кажучи, відносні до документа координати pageX/Y відраховуються від лівого верхнього кута документа і не змінюються під час прокручування сторінки, тоді як clientX/Y відраховуються від лівого верхнього кута поточного вікна. Коли сторінка прокручується, вони змінюються.

Наприклад, якщо у нас є вікно розміром 500x500, а миша знаходиться в лівому верхньому куті, то clientX і clientY дорівнюють 0, незалежно від того, як прокручується сторінка.

А якщо миша знаходиться в центрі, то clientX і clientY дорівнюють 250, незалежно від того, яке місце в документі це. У цьому аспекті вони схожі на position:fixed.

Наведіть курсор миші на поле введення, щоб побачити clientX/clientY (приклад знаходиться в iframe, тому координати є відносно цього iframe):

<input onmousemove="this.value=event.clientX+':'+event.clientY" value="Наведіть курсор на мене">

Запобігання виділенню при наведенні миші

Подвійний клік миші має побічний ефект, який може створювати незручності в деяких інтерфейсах: він виділяє текст.

Наприклад, подвійний клік на тексті нижче виділяє його на додаток до нашого обробника:

<span ondblclick="alert('dblclick')">Клікни мене двічі</span>

Якщо натиснути ліву кнопку миші і, не відпускаючи її, пересунути, це також додасть виділення, часто небажане.

Існує кілька способів запобігти виділенню, про які ви можете прочитати в розділі Selection і Range.

У цьому конкретному випадку найрозумнішим способом є запобігання дії браузера на mousedown. Таким чином ми запобігнемо двом виділенням:

До...
<b ondblclick="alert('Клік!')" onmousedown="return false">
  Клікни мене двічі
</b>
...Після

Тепер виділений жирним елемент не виділяється подвійними кліком, і натискання на ньому лівої кнопки не почне виділення.

Зверніть увагу: текст всередині нього все ще можна виділити. Однак виділення слід починати не з самого тексту, а до або після нього. Зазвичай, це нормально сприймається користувачами.

Запобігання копіювання

Якщо ми хочемо вимкнути виділення, щоб захистити вміст нашої сторінки від копіювання, ми можемо використовувати іншу подію: oncopy.

<div oncopy="alert('Копіювання заборонено!');return false">
  Шановний користувач,
  Копіювання заборонено.
  Якщо ви знаєте JS або HTML, ви можете отримати все з джерела сторінки.
</div>

Якщо ви спробуєте скопіювати фрагмент тексту в <div>, це не спрацює, оскільки типову дію oncopy заборонено.

Безумовно, користувач має доступ до HTML-джерела сторінки, і може взяти звідти вміст, але не всі знають, як це зробити.

Підсумки

Події миші мають такі властивості:

  • Кнопка: button.

  • Клавіші-модифікатори (true, якщо їх натиснути): altKey, ctrlKey, shiftKey і metaKey (Mac).

    • Якщо ви хочете працювати з Ctrl, то не забувайте про користувачів Mac, вони зазвичай використовують Cmd, тому краще перевірити if (e.metaKey || e.ctrlKey).
  • Координати відносно Window: clientX/clientY.

  • Координати відносно Document: pageX/pageY.

Типова дія браузера mousedown – виділення тексту, якщо в інтерфейсі воно швидше заважає, йому слід запобігти.

У наступному розділі ми дізнаємось більше деталей про події, які слідують за рухом курсора, і про те, як відстежувати зміни елементів під ним.

Завдання

важливість: 5

Створіть список, де елементи можна виділити, як у файлових менеджерах.

  • Клік на елементі списку виділяє лише цей елемент (додає клас .selected), знімає виділення з усіх інших.
  • Якщо клікнути за допомогою Ctrl (Cmd для Mac), тоді виділення елемента перемикається, але інші елементи не змінюються.

Демо:

P.S. Для цього завдання ми можемо припустити, що елементи списку є лише текстовими. Немає вкладених тегів.

P.P.S. Спробуйте запобігти виділенню тексту в браузері за кліками.

Відкрити пісочницю для завдання.

Навчальна карта

Коментарі

прочитайте це, перш ніж коментувати…
  • Якщо у вас є пропозиції, щодо покращення підручника, будь ласка, створіть обговорення на GitHub або одразу створіть запит на злиття зі змінами.
  • Якщо ви не можете зрозуміти щось у статті, спробуйте покращити її, будь ласка.
  • Щоб вставити код, використовуйте тег <code>, для кількох рядків – обгорніть їх тегом <pre>, для понад 10 рядків – використовуйте пісочницю (plnkr, jsbin, codepen…)