Ранее в скриптах мы с вами часто использовали событие click – щелчок левой кнопкой мыши. В действительности – это составное событие, то есть, мы сначала нажимаем левую кнопку, затем отпускаем и только потом генерируется событие click. Причем в момент нажатия и отпускания кнопки возникают свои события:
mousedown/mouseup – нажатие и отпускание кнопки мыши.
Давайте в этом убедимся, возьмем вот такой HTML-документ:
В нем прописаны три обработчика на события click, mousedown и mouseup. Теперь кликнем по документу и в консоли увидим эти три события в порядке:
mousedown → mouseup → click
И это стандартизированный порядок: данные события возникают именно в такой последовательности. Для двойного клика тоже есть свое событие dblclick. Добавим его:
Теперь при двойном щелчке левой кнопкой мыши, мы видим такие события:
Эта последовательность также стандартизирована и события следуют друг за другом именно в таком порядке.
Теперь мы знаем какая кнопка мыши была нажата при возникновении данных событий.
Обрабатывать их достаточно просто, например так:
Мы здесь увидим сообщение «click» только если при клике были нажаты клавиши Shift и Ctrl.
Обратите внимание, когда мы пишем браузерные скрипты, то предполагаем, что они могут быть запущены на самых разных компьютерах с разными ОС, в том числе и Mac OS. Так вот, в ОС Mac пользователи вместо клавиши Ctrl нажимают на клавишу Cmd. И это следует учесть в скрипте, например, так:
Здесь мы проверяем не только Ctrl, но и свойство metaKey, отвечающее за нажатие на кнопку Cmd.
Также не нужно забывать и про пользователей мобильных устройств с отсутствующей клавиатурой. Так что они вряд ли смогут перемещать курсор мыши с нажатой клавишей Shift!
Например, для события mousemove будем выводить координаты курсора мыши в консоль:
Аналогично работают свойства pageX/pageY.
Например, у нас есть вот такое содержимое страницы:
И добавим обработчик события mouseover:
Давайте посмотрим, как это все будет работать. Объясняем.
Изменим в обработчике событие на mouseout и обновим документ. Видите, эти свойства работают именно так, что логично, так как событие mouseout возникает при покидании элемента, значит target будет тем элементом, с которого мы ушли, а relatedTarget – элементом где мы сейчас находимся.
То при наведении курсора на дочерний элемент p, увидим событие mouseover. Причем при переходе с дочернего элемента и обратно также генерируется это событие.
Если же мы хотим чтобы событие возникало один раз при наведении на него курсора мыши, то можно воспользоваться эквивалентными событиями:
mouseenter и mouseleave
Например, заменим событие mouseover на mouseenter, получим. Смотрите, теперь при переходе между дочерним элементом и div дополнительных событий не возникает. Аналогично работает и mouseleave.
Из-за того, что события mouseenter и mouseleave не всплывают, они не могут быть делегированы обработчику верхнего уровня, например, для объекта document:
так работать не будет. Но, если прописать событие
то мы его успешно перехватываем. Вот эти ограничения следует учитывать при использовании событий mouseenter и mouseleave.
Итак, на этом занятии мы с вами рассмотрели работу следующих событий мыши:
При нажатии и отпускании кнопки мыши
При щелчке левой кнопкой мыши
При двойном щелчке левой кнопкой мыши
При перемещении мыши
При наведении и покидании курсора мыши на элемент
mouseenter/mouseleave
При наведении и покидании курсора мыши на элемент (не всплывают и не учитывают дочерние элементы)
Также мы с вами рассмотрели следующие свойства объекта event:
Номер нажатой кнопки мыши
event.shiftKey/altKey/ctrlKey/metaKey
Флаги, указывающие на соответствующую нажатую кнопку клавиатуры
event.clientX/clientY
Координаты курсора мыши относительно окна браузера
Координаты курсора мыши относительно HTML-документа
event.target/ relatedTarget
Ссылки на объекты в зависимости от типов события: mouseover/mouseout
Видео по теме
JavaScipt (DOM) #1: объектная модель документа DOM и BOM
JavaScipt (DOM) #3: методы поиска элементов в DOM: querySelector, querySelectorAll, getElementById
В этой главе мы более подробно рассмотрим события, возникающие при движении указателя мыши над элементами страницы.
События mouseover/mouseout, relatedTarget
Событие mouseover происходит в момент, когда курсор оказывается над элементом, а событие mouseout – в момент, когда курсор уходит с элемента.
Для события mouseover :
Для события mouseout наоборот:
В примере ниже каждое лицо и его черты – отдельные элементы. При движении указателя по этим элементам в текстовом поле отображаются происходящие события.
Каждое из них содержит информацию о target и relatedTarget :
Это нормально и означает, что указатель мыши перешёл не с другого элемента, а из-за пределов окна браузера. Или же, наоборот, ушёл за пределы окна.
Пропуск элементов
Событие mousemove происходит при движении мыши. Однако, это не означает, что указанное событие генерируется при прохождении каждого пикселя.
Это означает, что если пользователь двигает мышкой очень быстро, то некоторые DOM-элементы могут быть пропущены:
Это хорошо с точки зрения производительности, потому что если промежуточных элементов много, вряд ли мы действительно хотим обрабатывать вход и выход для каждого.
С другой стороны, мы должны иметь в виду, что указатель мыши не «посещает» все элементы на своём пути. Он может и «прыгать».
Вы можете проверить это «вживую» на тестовом стенде ниже.
В его HTML есть два элемента,
Также попробуйте поставить курсор на внутренний элемент, а затем очень быстро сделайте движение мышкой вниз через внешний элемент. Если у вас получится достаточно быстро, то на родительском элементе не будет сгенерировано никаких событий. То есть, мышь пройдёт через внешний элемент, не замечая его.
Несмотря на то, что при быстрых переходах промежуточные элементы могут игнорироваться, в одном мы можем быть уверены: элемент может быть пропущен только целиком.
Событие mouseout при переходе на потомка
Важная особенность события mouseout – оно генерируется в том числе, когда указатель переходит с элемента на его потомка.
Это выглядит странно, но легко объясняется.
По логике браузера, курсор мыши может быть только над одним элементом в любой момент времени – над самым глубоко вложенным и верхним по z-index.
Таким образом, если курсор переходит на другой элемент (пусть даже дочерний), то он покидает предыдущий.
Обратите внимание на важную деталь.
Вы можете наглядно увидеть это в примере ниже:
При переходе мышью с внешнего элемента на внутренний, вы увидите сразу два события: mouseout [target: parent] (ушли с родителя) и mouseover [target: child] (перешли на потомка, событие всплыло).
При переходе с родителя элемента на потомка – на родителе сработают два обработчика: и mouseout и mouseover :
Если при уходе с элемента что-то происходит, например, запускается анимация, то такая интерпретация происходящего может давать нежелательные побочные эффекты.
Чтобы этого избежать, можно смотреть на relatedTarget и, если мышь всё ещё внутри элемента, то игнорировать такие события.
События mouseenter и mouseleave
Но есть и пара важных отличий:
События mouseenter/mouseleave предельно просты и понятны.
Событие mouseleave происходит, когда курсор покидает элемент.
Как вы сами можете увидеть, генерируются только события, связанные с движением курсора относительно верхнего
Делегирование событий
События mouseenter/leave просты и легки в использовании. Но они не всплывают. Таким образом, мы не можем их делегировать.
Представьте ситуацию, когда мы хотим обрабатывать события, сгенерированные при движении курсора по ячейкам таблицы. И в таблице сотни ячеек.
Очевидное решение – определить обработчик на родительском элементе
и там обрабатывать возникающие события. Но, так как mouseenter/leave не всплывают, то если событие происходит на ячейке
, то только обработчик на
может поймать его.
Обработчики событий mouseenter/leave на
срабатывают, если курсор оказывается над таблицей в целом или же уходит с неё. Невозможно получить какую-либо информацию о переходах между ячейками внутри таблицы.
Начнём с простых обработчиков, которые выделяют текущий элемент под указателем мыши:
Вот они в действии. При переходе между элементами этой таблицы, текущий будет подсвечен:
В нашем случае мы хотим обрабатывать переходы именно между ячейками
: вход на ячейку и выход с неё. Прочие переходы, в частности, внутри ячейки
или вообще вне любых ячеек, нас не интересуют, хорошо бы их отфильтровать.
Можно достичь этого так:
Вот пример кода, учитывающего все ситуации:
Полный пример со всеми деталями:
Попробуйте подвигать курсор между ячейками и внутри них. Быстро или медленно – без разницы. В отличие от предыдущего примера выделяется только сама ячейка
.
Итого
Особенности, на которые стоит обратить внимание:
События mouseover/out возникают, даже когда происходит переход с родительского элемента на потомка. С точки зрения браузера, курсор мыши может быть только над одним элементом в любой момент времени – над самым глубоко вложенным.
События mouseenter/leave в этом отличаются. Они генерируются, когда курсор переходит на элемент в целом или уходит с него. Также они не всплывают.
Задачи
Улучшенная подсказка
Это похоже на задачу Поведение «подсказка», но здесь элементы с подсказками могут быть вложены друг в друга. Показываться должна подсказка на самом глубоко вложенном элементе.
Только одна подсказка может быть показана в любой момент времени.
Результат в iframe:
«Умная» подсказка
Напишите функцию, которая показывает подсказку над элементом только в случае, когда пользователь передвигает мышь на него, но не через него.
Другими словами, если пользователь подвинул курсор на элементе и остановился – показывать подсказку. А если он просто быстро провёл курсором по элементу, то не надо ничего показывать. Кому понравится лишнее мелькание?
Технически, мы можем измерять скорость прохода курсора мыши над элементом, и если она низкая, то можно посчитать, что пользователь остановил курсор над элементом, и показать ему подсказку. А если скорость высокая, то тогда не показывать.
Его настройки options :
Пример использования такого объекта для показа подсказки:
Если двигать курсор над «часами» быстро, то ничего не произойдёт, а если вы замедлите движение курсора над элементом или остановите его, то будет показана подсказка.
Обратите внимание: подсказка не должна пропадать (мигать), когда курсор переходит между дочерними элементами часов.
Алгоритм выглядит просто:
Но как измерить скорость?
Первая идея может быть такой: запускать нашу функцию каждые 100ms и находить разницу между прежними и текущими координатами курсора. Если она мала, то значит и скорость низкая.
MouseEvent.altKey (en-US) Только для чтения Возвращает значение true, если клавиша alt была нажата во время движения мыши. MouseEvent.button Только для чтения Представляет код клавиши, нажатой в то время, когда произошло событие мыши. MouseEvent.buttons Только для чтения
Отображает, какие клавиши были нажаты во время движения мыши.
MouseEvent.clientX Только для чтения Отображение X координат курсора мыши в локальной системе координат (DOM контент). MouseEvent.clientY Только для чтения Отображение Y координат курсора мыши в локальной системе координат (DOM контент). MouseEvent.ctrlKey Только для чтения Возвращает значение true, если клавиша control была нажата во время движения мыши. MouseEvent.metaKey (en-US) Только для чтения Возвращает значение true, если клавиша meta была нажата во время движения мыши. MouseEvent.movementX (en-US) Только для чтения Отображает X координат указателя мыши относительно позиции последнего mousemove (en-US) события. MouseEvent.movementY (en-US) Только для чтения Отображает Y координат указателя мыши относительно позиции последнего mousemove (en-US) события. MouseEvent.offsetX Только для чтения Отображает X координат указателя мыши относительно позиции границы отступа целевого узла. MouseEvent.offsetY (en-US) Только для чтения Отображает Y координат указателя мыши относительно позиции границы отступа целевого узла. MouseEvent.pageX Только для чтения Отображает X координат указателя мыши относительно всего документа. MouseEvent.pageY (en-US) Только для чтения Отображает Y координат указателя мыши относительно всего документа. MouseEvent.region (en-US) Только для чтения Возвращает id затронутого событием региона. Если ни какой регион затронут не был, возвращает null. MouseEvent.relatedTarget (en-US) Только для чтения Второстепенная цель события, если таковая есть. MouseEvent.screenX Только для чтения Отображает X координат указателя мыши в пространстве экрана. MouseEvent.screenY (en-US) Только для чтения Отображает Y координат указателя мыши в пространстве экрана. MouseEvent.shiftKey Только для чтения Возвращает true если клавиша shift была нажата, когда произошло событие мыши. MouseEvent.which Только для чтения Возвращает код последней нажатой клавиши, когда произошло событие мыши. MouseEvent.mozPressure Только для чтения Отображает давление которое было осуществлено при нажатии. Значение будет между 0.0 (минимальное давление) и 1.0 (максимальное давление). MouseEvent.mozInputSource (en-US) Только для чтения
The type of device that generated the event (one of the MOZ_SOURCE_* constants listed below). This lets you, for example, determine whether a mouse event was generated by an actual mouse or by a touch event (which might affect the degree of accuracy with which you interpret the coordinates associated with the event).
Константы
Методы
MouseEvent.getModifierState() (en-US) Returns the current state of the specified modifier key. See the KeyboardEvent.getModifierState (en-US) () for details. MouseEvent.initMouseEvent() (en-US) Initializes the value of a MouseEvent created. If the event has already being dispatched, this method does nothing.
Примеры
Данный пример демонстрирует симуляцию нажатия левой клавиши мыши (событие мыши генерируется программно) по чекбоксу используя методы DOM.
Нажмите на кнопку, чтобы посмотреть, как работает пример.
В этой главе мы более детально рассмотрим события мыши и их свойства.
Сразу заметим: эти события бывают не только из-за мыши, но и эмулируются на других устройствах, в частности, на мобильных, для совместимости.
Типы событий мыши
Мы можем разделить события мыши на две категории: «простые» и «комплексные».
Простые события
Самые часто используемые простые события:
mousedown/mouseup Кнопка мыши нажата/отпущена над элементом. mouseover/mouseout Курсор мыши появляется над элементом и уходит с него. mousemove Каждое движение мыши над элементом генерирует это событие. contextmenu Вызывается при попытке открытия контекстного меню, как правило, нажатием правой кнопки мыши. Но, заметим, это не совсем событие мыши, оно может вызываться и специальной клавишей клавиатуры.
…Есть также несколько иных типов событий, которые мы рассмотрим позже.
Комплексные события
Комплексные события состоят из простых, поэтому в теории мы могли бы без них обойтись. Но хорошо, что они существуют, потому что работать с ними очень удобно.
Порядок событий
Одно действие может вызвать несколько событий.
Кликните на кнопку ниже, и вы увидите события. Также попробуйте двойной клик.
В окне теста ниже все события мыши записываются, и если задержка между ними более 1 секунды, то они разделяются горизонтальной чертой.
Получение информации о кнопке: which
Есть три возможных значения:
Средняя кнопка сейчас – скорее экзотика, и используется очень редко.
Модификаторы: shift, alt, ctrl и meta
Все события мыши включают в себя информацию о нажатых клавишах-модификаторах.
Свойства объекта события:
Например, кнопка внизу работает только при комбинации Alt + Shift +клик:
Поэтому, если мы хотим поддерживать такие комбинации, как Ctrl +клик, то для Mac имеет смысл использовать Cmd +клик. Это удобней для пользователей Mac.
Комбинации клавиш на клавиатуре – это хорошее дополнение к рабочему процессу. Если у пользователя есть клавиатура – они работают. Ну а если на его устройстве её нет – должен быть другой способ сделать то же самое.
Координаты: clientX/Y, pageX/Y
Все события мыши имеют координаты двух видов:
Отключаем выделение
Двойной клик мыши имеют побочный эффект, который может быть неудобен в некоторых интерфейсах: он выделяет текст.
Например, двойной клик на текст ниже выделяет его в дополнение к нашему обработчику:
Если зажать левую кнопку мыши и, не отпуская кнопку, провести мышью, то также будет выделение, которое в интерфейсах может быть «не кстати».
Есть несколько способов запретить выделение, о которых вы можете прочитать в главе Selection и Range.
Теперь выделенный жирным элемент не выделяется при двойном клике, а также на нём нельзя начать выделение, зажав кнопку мыши.
Заметим, что текст внутри него по-прежнему можно выделить, если начать выделение не на самом тексте, а до него или после. Обычно это нормально воспринимается пользователями.
Если вы попытаетесь скопировать текст в
Конечно, пользователь имеет доступ к HTML-коду страницы и может взять текст оттуда, но не все знают, как это сделать.
Итого
События мыши имеют следующие свойства:
Действие по умолчанию события mousedown – начало выделения, если в интерфейсе оно скорее мешает, его можно отменить.
В следующей главе мы поговорим о событиях, которые возникают при передвижении мыши, и об отслеживании смены элементов под указателем.
Задачи
Выделяемый список
Создайте список, в котором элементы могут быть выделены, как в файловых менеджерах.
Основной способ реагирования на ввод с помощью мыши заключается в обработке событий мыши. В следующей таблице показаны события мыши и описание их возникновения.
Событие мыши
Описание
Click
Это событие возникает при отпускании кнопки мыши, как правило, перед событием MouseUp. Обработчик этого события принимает аргумент типа EventArgs. Обрабатывать это событие следует в случае, если нужно только определить, когда происходит щелчок.
MouseClick
Это событие возникает, когда пользователь щелкает элемент управления. Обработчик этого события принимает аргумент типа MouseEventArgs. Обрабатывать это событие следует в случае, когда необходимо получить сведения о мыши при щелчке.
DoubleClick
Это событие происходит при двойном щелчке элемента управления. Обработчик этого события принимает аргумент типа EventArgs. Обрабатывать это событие следует в случае, если нужно только определить, когда происходит двойной щелчок.
MouseDoubleClick
Это событие возникает, когда пользователь дважды щелкает мышью. Обработчик этого события принимает аргумент типа MouseEventArgs. Обрабатывать это событие следует в случае, когда необходимо получить сведения о мыши при двойном щелчке.
MouseDown
Это событие происходит при нажатии пользователем кнопки мыши, когда указатель мыши находится на элементе управления. Обработчик этого события принимает аргумент типа MouseEventArgs.
MouseEnter
Это событие возникает, когда указатель мыши перемещается на границу или клиентскую область элемента управления в зависимости от типа элемента управления. Обработчик этого события принимает аргумент типа EventArgs.
MouseHover
Это событие возникает при остановке и помещении указателя мыши на элемент управления. Обработчик этого события принимает аргумент типа EventArgs.
MouseLeave
Это событие возникает, когда указатель мыши покидает границу или клиентскую область элемента управления в зависимости от типа элемента управления. Обработчик этого события принимает аргумент типа EventArgs.
MouseMove
Это событие возникает при перемещении указателя мыши на элемент управления. Обработчик этого события принимает аргумент типа MouseEventArgs.
MouseUp
Это событие возникает, когда указатель мыши находится на элементе управления и пользователь отпускает кнопку мыши. Обработчик этого события принимает аргумент типа MouseEventArgs.
MouseWheel
Это событие возникает, когда пользователь вращает колесико мыши, когда фокус находится на элементе управления. Обработчик этого события принимает аргумент типа MouseEventArgs. Для определения того, насколько прокручено колесико мыши, можно использовать свойство Delta элемента MouseEventArgs.
Сведения о мыши
Объект MouseEventArgs отправляется обработчикам событий мыши, связанных с нажатием кнопки мыши и отслеживанием ее движений. Объект MouseEventArgs предоставляет сведения о текущем состоянии мыши, включая положение указателя мыши в клиентских координатах, какие кнопки мыши нажаты и произошла ли прокрутка колесика мыши. Некоторые события мыши, например те, которые возникают, когда указатель мыши пересек границы элемента управления, отправляют обработчику событий объект EventArgs без подробных сведений.
Если нужно знать текущее состояние кнопок мыши или положение ее указателя, но при этом избежать обработки события мыши, можно также использовать свойства MouseButtons и MousePosition класса Control. Свойство MouseButtons возвращает сведения о том, какие кнопки мыши в настоящее время нажаты. Свойство MousePosition возвращает экранные координаты указателя мыши, которые эквивалентны значению, возвращаемому методом Position.
Преобразование между экранными и клиентскими координатами
Так как некоторые сведения о положении мыши представлены в клиентских координатах, а другие — в экранных, может потребоваться преобразовать точку из одной системы координат в другую. Это легко сделать с помощью методов PointToClient и PointToScreen, доступных в классе Control.
Стандартное поведение события щелчка
Если требуется обрабатывать события щелчка мыши в определенном порядке, необходимо знать порядок, в котором вызываются события щелчка в элементах управления Windows Forms. Когда любая поддерживаемая кнопка мыши нажимается и отпускается, все элементы управления Windows Forms, кроме отмеченных в списке ниже, вызывают события щелчка в одном и том же порядке. Ниже приведен порядок событий, вызываемых одинарным щелчком мыши.
Ниже приведен порядок событий, вызываемых двойным щелчком мыши.
Особые элементы управления
Поведение перечисленных ниже элементов управления при щелчке мыши не соответствует стандартному.
Если пользователь щелкает поле редактирования, кнопку или элемент в списке, то для элемента управления ComboBox возникают описанные ниже события.
Если пользователь щелкает любое место внутри этих элементов управления, то возникают описанные ниже события.
Указанные ниже события возникают только в том случае, если пользователь щелкает элементы в ListView. Если пользователь щелкает мышью в любом другом месте элемента управления, то события не вызываются. В дополнение к событиям, описанным ниже, существуют события BeforeLabelEdit и AfterLabelEdit, которые могут представлять интерес, если нужно выполнять проверку с помощью элемента управления ListView.
Указанные ниже события возникают только в том случае, если пользователь щелкает сами элементы или справа от них в элементе управления TreeView. Если пользователь щелкает мышью в любом другом месте элемента управления, то события не вызываются. В дополнение к событиям, описанным ниже, существуют события BeforeCheck, BeforeSelect, BeforeLabelEdit, AfterSelect, AfterCheck и AfterLabelEdit, которые могут представлять интерес, если нужно выполнять проверку с помощью элемента управления TreeView.
Поведение отрисовки для переключателей
Переключатели, такие как элементы управления, производные от класса ButtonBase, имеют описанное ниже нестандартное поведение отрисовки в сочетании с событиями щелчка.
Пользователь нажимает кнопку мыши.
Элемент управления отрисовывается в состоянии «нажато».
Пользователь отпускает кнопку мыши.
Элемент управления отрисовывается в состоянии «отпущено».
Если пользователь перемещает указатель за границы переключателя при нажатой кнопке мыши (например, перемещает указатель мыши за границы элемента управления Button, когда он нажат), переключатель будет отрисовываться в состоянии «отпущено» и происходит только событие MouseUp. События Click и MouseClick в этой ситуации не наступают.