Миша кліки, кнопка, координати
У цьому розділі ми глибше розберемося зі списком подій миші, розглянемо їх загальні властивості, а також ті події, які пов'язані з кліком.
Умовно можна розділити події на два типи: «прості» і «комплексні».
mousedown Кнопка миші натиснута над елементом. mouseup Кнопка миші відпущена над елементом. mouseover Миша з'явилася над елементом. mouseout Миша пішла з елемента. mousemove Кожен рух миші над елементом генерує ця подія.
click Викликається при кліці мишею, тобто при mousedown. а потім mouseup на одному елементі contextmenu Викликається при натисканні правою кнопкою миші на елементі. dblclick Викликається при подвійному натисканні по елементу.
Комплексні можна скласти з простих, тому в теорії можна було б обійтися взагалі без них. Але вони є, і це добре, тому що з ними зручніше.
Одна дія може викликати кілька подій.
Наприклад, клік викликає спочатку mousedown при натисканні, а потім mouseup і click при відпуску кнопки.
У тих випадках, коли одна дія генерує кілька подій, їх порядок фіксований. Тобто, обробники викликаються в порядку mousedown → mouseup → click.
Клацніть по кнопці нижче і ви побачите, які при цьому відбуваються події. Спробуйте також подвійний клік.
На тест-стенді нижче все мишачі події записуються, і якщо між подіями проходить більше 1 секунди, то вони для зручності читання відокремлюються лінією. Також присутні властивості which / button. за якими можна визначити кнопку миші. Ми їх розглянемо далі.
Кожна подія обробляється незалежно.
Наприклад, при кліці події mouseup + click виникають одночасно, але обробляються послідовно. Спочатку повністю завершується опрацювання mouseup. потім запускається click.
При обробці подій, пов'язаних з кліками миші, буває важливо знати, яка кнопка натиснута.
Для отримання кнопки миші в об'єкті event є властивість which.
На практиці воно використовується рідко, тому що зазвичай обробник вішається або onclick - тільки на ліву кнопку миші, або oncontextmenu - тільки на праву.
Можливі такі значення:
- event.which == 1 - ліва кнопка
- event.which == 2 - середня кнопка
- event.which == 3 - права кнопка
Це властивість не підтримується IE8-, але його можна отримати способом, описаним в кінці розділу.
Ця подія спрацьовує при натисканні правою кнопкою миші:
При кліці на кнопку вище після обробника oncontextmenu буде показано звичайне контекстне меню, яке браузер завжди показує при натисканні правою кнопкою. Це є його дією за замовчуванням.
Якщо ми не хочемо, щоб показувалося вбудоване меню, наприклад тому що показуємо своє, специфічне для нашого застосування, то можна скасувати дію за замовчуванням.
У прикладі нижче вбудоване меню показано не буде:
У всіх подіях миші присутня інформація про натиснутих клавішах-модифікатори.
Наприклад, кнопка нижче спрацює тільки на Alt + Shift + Клік:
Увага: на Mac замість Ctrl використовується Cmd
На комп'ютерах під управлінням Windows і Linux є спеціальні клавіші Alt. Shift і Ctrl. На Mac є ще одна спеціальна клавіша: Cmd. якої відповідає властивість metaKey.
У більшості випадків там, де під Windows / Linux використовується Ctrl. на Mac використовується Cmd. Там, де користувач Windows натискає Ctrl + Enter або Ctrl + A. користувач Mac натисне Cmd + Enter або Cmd + A. і так далі, майже завжди Cmd замість Ctrl.
Тому, якщо ми хочемо підтримувати поєднання Ctrl + click або інші подібні, то під Mac має сенс використовувати Cmd + click. Користувачам Mac це буде набагато комфортніше.
Більш того, навіть якщо б ми хотіли б змусити користувачів Mac використовувати саме Ctrl + click - це було б важко. Справа в тому, що звичайний клік з затиснутим Ctrl під Mac працює як правий клік і генерує подія oncontextmenu. а зовсім не onclick. як під Windows / Linux.
Рішення - щоб користувачі обох операційних систем працювали з комфортом, в парі з ctrlKey потрібно обов'язково використовувати metaKey.
В JS-коді це означає, що для зручності користувачів Mac потрібно перевіряти if (event.ctrlKey || event.metaKey).
Все мишачі події надають поточні координати курсору в двох видах: щодо вікна і щодо документа.
Пара властивостей clientX / clientY містить координати курсору щодо поточного вікна.
При цьому, наприклад, якщо ваше вікно розміром 500x500, а миша знаходиться в центрі, тоді і clientX і clientY дорівнюватимуть 250.
Можна як завгодно прокручувати сторінку, але якщо не рухати при цьому миша, то координати курсору clientX / clientY не зміняться, тому що вони вважаються відносно вікна, а не документа.
Проведіть мишею над полем введення, щоб побачити clientX / clientY:
У тій же системі координат працює і метод elem.getBoundingClientRect (). повертає координати елемента, а також position: fixed.
Координати курсору щодо документа знаходяться у властивостях pageX / pageY.
Так як ці координати - щодо лівого-верхнього вузла документа, а не вікна, то вони враховують прокрутку. Якщо прокрутити сторінку, а миша не чіпати, то координати курсору pageX / pageY зміняться на величину прокрутки, вони прив'язані до конкретної точки в документі.
У IE8- цих властивостей немає, але можна отримати їх способом, описаним в кінці розділу.
Проведіть мишею над полем введення, щоб побачити pageX / pageY (крім IE8-):
У тій же системі координат працює position: absolute. якщо елемент позиціонується щодо документа.
Застаріли: x, y, layerX, layerY
Деякі браузери підтримують властивості event.x / y. event.layerX / layerY.
Ці властивості застаріли, вони нестандартні і не додають нічого до описаних вище. Використовувати їх не варто.
Всі браузери, крім IE8-, генерують dblclick на додаток до інших подій.
- mousedown (натиснув)
- mouseup + click (віджав)
- mousedown (натиснув)
- mouseup + click + dblclick (віджав).
IE8- на другому кліці не генерує mousedown і click.
- mousedown (натиснув)
- mouseup + click (віджав)
- (Натиснув вдруге, без події)
- mouseup + dblclick (віджав).
Тому відловити подвійний клік в IE8-, відстежуючи лише click. не можна, адже при другому натисканні його немає. Потрібно саме подія dblclick.
У старих IE8- не підтримує властивість which. а замість нього використовувалося властивість button. яке є 3-х бітовим числом, в якому кожному біту відповідає кнопка миші. Біт встановлений в 1, тільки якщо відповідна кнопка натиснута.
Щоб його розшифрувати - потрібна побітова операція ( «Бітове І»):
- . (button 1) == true (1-й біт встановлений), якщо натиснута ліва кнопка,
- . (button 2) == true (2-й біт встановлений), якщо натиснута права кнопка,
- . (button 4) == true (3-й біт встановлений), якщо натиснута середня кнопка.
Що цікаво, при цьому ми можемо дізнатися, чи були дві кнопки натиснуті одночасно, в той час як стандартний which такої можливості не дає. Так що, в певному сенсі, властивість button - більш потужне.
Можна легко зробити функцію, яка буде ставити властивість which з button. якщо його немає:
В IE до версії 9 не підтримуються властивості pageX / pageY. але їх можна отримати, додавши до clientX / clientY величину прокрутки сторінки.
Більш докладно про її обчисленні ви можете прочитати в розділі прокрутка сторінки.
Ми ж тут наведемо готовий варіант, який дозволяє нам отримати pageX / pageY для старих і зовсім старих IE:
Події миші мають такі властивості:
- Кнопка миші: which (для IE8-: потрібно ставити з button)
- Елемент, що викликав подія: target
- Координати, щодо вікна: clientX / clientY
- Координати, щодо документа: pageX / pageY (для IE8-: потрібно ставити по clientX / Y і прокручуванні)
- Якщо затиснута спец. клавіша, то варто відповідне властивість: altKey. ctrlKey. shiftKey або metaKey (Mac).
- Для підтримки Ctrl + click не забуваємо перевірити if (e.metaKey || e.ctrlKey). щоб користувачі Mac теж були задоволені.
Це завдання складається з трьох частин.
- Зробіть список, елементи якого можна виділяти кліком.
- Додайте мульти-виділення. Якщо клік з натиснутим Ctrl (Cmd під Mac), то елемент додається-видаляється з виділених.
- Додайте виділення проміжків. Якщо відбувається клік з натиснутим Shift. то до виділення додається проміжок елементів від попереднього клікнути до цього. При цьому не важливо, яка саме дія робив попередній клік. Це схоже на те, як працює файловий менеджер в ряді ОС, але трохи простіше, так як конкретна реалізація виділень різниться у різних ОС, і її точне відтворення не входить в цю задачу.
P.S. У цьому завданні можна вважати, що в елементах списку може бути тільки текст, без вкладених тегів.
P.P.S. Обробка одночасного натискання Ctrl (Cmd) і Shift може бути будь-хто.
Однак, проблема в тому, що приховування-розкриття відбувається навіть при кліці поза заголовка. на порожньому просторі праворуч від нього.
Як приховувати / розкривати дітей тільки при кліці на заголовок?
У задачі Розкривне дерево це вирішено так: заголовки загорнуті в елементи SPAN і перевіряються кліки тільки на них. Уявімо на хвилину, що ми не хочемо обертати текст в SPAN. а хочемо залишити як є. Наприклад, з міркувань продуктивності, якщо дерево і так дуже велике, адже обертання всіх заголовків в SPAN збільшить кількість DOM-вузлів в 2 рази.
Вирішіть задачу без обгортання заголовків в SPAN. використовуючи роботу з координатами.
Вихідний документ містить клікабельно дерево.
P.S. Завдання - швидше на кмітливість, проте підхід може бути корисний в реальному житті.
У події кліка є координати. Перевірте по ним, чи потрапив клік на заголовок.
Найглибший вузол на координатах можна отримати викликом document.elementFromPoint (clientX, clientY).
... Але заголовок є текстовим вузлом, тому ця функція для нього працювати не буде. Однак це, все ж, можна обійти. Як?
Підказка 2
Можна при кліці на LI зробити тимчасовий SPAN і перемістити в нього текстовий вузол-заголовок.
Після цього перевірити, чи потрапив клік в нього і повернути все як було.
На кроці 3 текстовий вузол виймається назад з SPAN. все повертається в початковий стан.