Обработка событий в jQuery, в принципе, очень похожа на обработку событий в нативном JavaScript, т.к. вам придется:
- Указать элемент(-ы), для которых вы выполняете обработку события(-ий), используя для этого селекторы jQuery
- Указать, какое событие вы будете обрабатывать.
- Написать код в функции, которая и будет служить обработчиком события.
jQuery предлагает нам такие способы назначения обработчиков событий:
$('селектор').событие(function(){ ... })
$('селектор').on('событие', function(){ ... })
$('селектор').bind('событие', function(){ ... })
(устаревший вариант)$('селектор').one('событие', function(){ ... })
- однократное срабатывание события
Также вы можете отменить назначенные обработчики события(-й) с помощью методов:
Обработчик типа событие()
Для того чтобы назначить такой обработчик, необходимо написать код в виде вызова функции с названием (именем) события:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //общий вид обработчика события $('селектор').событие(function(){ //код функции }) //для события мыши $('селектор').mousemove(function(){ //код функции }) //для события клавиатуры $('селектор').keydown(function(){ //код функции }) //для события отправки формы $('form').submit(function(){ //код функции }) |
Пример обработки клика на элементе div:
1 2 3 | $('.my-div').click(function(){ $(this).toggleClass('bordered'); }); |
Пример в действии:
В этом примере мы обрабатываем событие клика (click
) для каждого из 3-х <div class="my-div">
. В коде мы обращаемся к тому элементу, на котором пользователь делает клик мышью с помощью ключевого слова $(this)
и вызываем для него метод toggleClass('bordered')
, который проверяет, есть ли у нашего div-a атрибут class="bordered"
и добавляет такой класс, если его не было, а при повторном клике на этом div-е удаляет класс bordered
.
Обратите внимание, что в случае с использованием jQuery по сравнению с нативным JS, нам не нужно использовать цикл или метод forEach()
для перебора нескольких выбранных элементов и назначения обработчика для каждого из них. За нас это сделает jQuery.
Событие hover()
Такого события нет в нативном JavaScript. Зато есть псевдокласс :hover
, который вы наверняка использовали в CSS для создания эффектов при наведении мышью, особенно в сочетании с анимацией с помощью свойства transition. В этом случае вы должны понимать, что hover-эффекты всегда срабатывают при наведении и уведении курсора мыши на одном и том же объекте. То есть с точки зрения JavaScript это не что иное, как последовательная обработка 2-х событий - mouseover и mouseout
. В случае с jQuery - это обработка событий mouseenter
и mouseleave
, которые не распростаняются на вложенные элементы. Для каждого из событий нужна своя функция-обработчик, поэтому для обработки события hover()
в jQuery мы добавляем 2 функции:
1 2 3 4 5 6 7 | $("селектор").hover(function() { //код при наведении курсора мыши //обработка события mouseenter }, function() { //код при уведении курсора мыши //обработка события mouseleave }); |
Например, нам нужно сделать такие эффекты при наведении и уведении курсора мыши:
В коде придется поработать с css-свойствами и разметкой:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | <style> * {box-sizing: border-box} body { font-family: Tahoma; line-height: 150%; } .test { width: 90%; margin: 25px auto; } .slideBox { width: 480px; height: 290px; position: relative; overflow: hidden; float: left; margin: 0 25px 25px 0; border: 5px solid #ccc; padding: 10px; background-color: #ac1259; color: #fff; } .slideBox h2 { margin: 0 0 15px; font-size: 20px; text-align: center; } .slideBox img { position: absolute; z-index: 2; width: 100%; left: 0; top: 0; } .slideBox .slide-content { position: absolute; z-index: 1; padding: 10px; } </style> <div class="test"> <div class="slideBox"> <img src="images/nano-gallery.jpg" alt="16 бриллиантовых плагинов jQuery" /> <div class="slide-content"> <h2>16 бриллиантовых плагинов jQuery</h2> <p>Из статьи вы узнаете о том, какие эффекты можно получить с помощью плагина <strong>jInvertscroll</strong>, как создать классический дизайн меню с помощью <strong>Slinky</strong>, какие вкладки можно сделать с помощью <strong>Tabslet</strong>, создать слайдер или карусель из изображений или текста, подключив плагин <strong>Slick</strong>, и много другой полезной информации</p> </div> </div> <!--end slideBox--> <div class="slideBox"> <img src="images/flip-clock.png" alt="Таймер обратного отсчета с помощью плагина jQuery" /> <div class="slide-content"> <h2>Таймер обратного отсчета с помощью плагина jQuery</h2> <p> jQuery плагин <strong>FlipClock</strong> построен на CSS3 + jQuery. Он позволяет задать несколько параметров, которые наверняка пригодятся вам для работы. В статье приведен пример, который часто используется на посадочных страницах, которые сообщают о том, что акция закончится, например, через 2 дня.</p> </div> </div> <!--end slideBox--> </div> <script> $(function() { $(".slideBox").hover(function() { $(this).find("img").stop().animate({ top: '-120%' }, 400); }, function() { $(this).find("img").stop().animate({ top: 0 }, 400); }); }) </script> |
Код в jQuery обрабатывает событие hover()
c помощью 2-х функций с подобным кодом: мы находим вложенное изображение по селектору тега img
с помощью строчки$(this).find("img")
, а затем методом animate()
поднимаем его вверх на 120% в первой функции и опускаем вниз до 0px во второй функции.
Обработчик события on()
Вы можете использовать альтернативный синтаксис для обработки событий в jQuery: вместо функции, которая содержит название события, используйте метод on()
. Он имеет такой синтаксис:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | //общий вид обработчика события $('селектор').on('событие', function(){ //код функции }) //для события мыши $('селектор').on('mousemove',function(){ //код функции }) //разные функции для разных обработчиков события $("селектор").on({ mouseenter: function() { //код функции 1 }, mouseleave: function() { //код функции 2 }, click: function() { //код функции 3 } }); //для нескольких событий с одной функцией-обработчиком $('селектор').on('keydown input cut',function(){ //код функции, который одинаково срабатывает для 3-х событий: //keydown, input и cut }) //для события изменения значения в элементах input и select формы $('form').on('change', 'input, select', function(){ //код функции, обрабатывающий событие для формы, //но делегирующий его для ее вложенных элементов input и select }) |
Если вы внимательно посмотрели приведенные выше примеры, то должны были заметить, что сфера действия обработчика события on()
в jQuery куда больше, чем обработчика вида событие()
: вы можете обработать не одно, а несколько событий, перечислив их в кавычках через пробел, а также делегировать обработку события от родительского элемента дочерним, просто указав их в качестве второго (необязательного) параметра.
Давайте рассмотрим, как это работает на примерах.
Добавление нескольких функций-обработчиков разных событий в методе on()
Рассмотрим пример с таблицей с некоторыми данными
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | <table id="sometable"> <thead> <tr> <th>Фамилия, Имя</th> <th>Телефон</th> <th>Email</th> </tr> </thead> <tbody> <tr> <td>Андреев Сергей</td> <td>322-44-55</td> <td>andreev@list.ru</td> </tr> <tr> <td>Золочева Марина</td> <td>322-67-58</td> <td>zolocheva_mm@gmail.com</td> </tr> <tr> <td>Иващенко Алексей</td> <td>322-44-67</td> <td>ivashenko@example.com</td> </tr> <tr> <td>Семенова Анастасия</td> <td>322-19-09</td> <td>semenova_a@yahoo.com</td> </tr> <tr> <td>Якунин Федор</td> <td>322-17-12</td> <td>yakunin@mail.ru</td> </tr> </tbody> </table> <script> $(document).ready(function() { $("#sometable tbody tr").on({ mouseenter: function() { $(this).css("background-color", "#7f3"); }, mouseleave: function() { $(this).css("background-color", ""); }, click: function() { $(this).css("background-color", "yellow"); $(this).unbind("mouseenter mouseleave"); } }); }) </script> |
Наведите несколько раз курсор мыши на строки таблицы, а затем кликните на любой из них и опять наведите - вы увидите, что цвет фона, указанный в обработчике события mouseenter
перестал применяться к строке с желтым фоном, т.к. при клике на ней мы не только добавили этот фон, но и удалили обработку событий mouseenter
и mouseleave
.
Фамилия, Имя | Телефон | |
---|---|---|
Андреев Сергей | 322-44-55 | andreev@list.ru |
Золочева Марина | 322-67-58 | zolocheva_mm@gmail.com |
Иващенко Алексей | 322-44-67 | ivashenko@example.com |
Семенова Анастасия | 322-19-09 | semenova_a@yahoo.com |
Якунин Федор | 322-17-12 | yakunin@mail.ru |
Пример с нескольких обработкой разных событий одной функцией в методе on()
Достаточно часто необходимо добавить несколько обработчиков событий для одного и того же элемента, и код при этом будет работать в одной и той же функции. Например, мы можем обрабатывать события ввода (input
), вставки (paste
) или нажатия клавиш (keydown
) для поля ввода. Все эти события мы должны указать при вызове метода on()
через пробел (хотя мне чаще хочется это сделать через запятую - нельзя!)
1 2 3 4 5 6 7 8 9 10 | <textarea rows="4" id="testInput"></textarea> <span id="symbols">0</span> символов <script> $(function() { $("#testInput").on('input paste keydown', countSymbols); function countSymbols() { $('#symbols').text($(this).val().length); } }); </script> |
Смотрим на реализацию примера:
Пример с делегированием событий
Задача состоит в следующем: необходимо поменять картинку и подпись под ней при клике на любой из радио-кнопок или при выборе любого пункта в выпадающем списке select
. Кроме того, для картинки нужно изменить атрибуты alt
(альтернативный текст, который отображается, если картинка не загружена) и title
, который отвечает за всплывающую подсказку и виден при наведении на картинку. Также необходимо будет изменить подпись под картинкой в заголовке <h3>
на тот текст, который находится рядом с отмеченной radio-кнопкой в элементе label
или в выбранном элементе option
в выпадающем списке.
Картинка имеет id="icon"
, заголовок <h3>
находится сразу за картинкой, т.е. это следующий за картинкой элемент, поэтому в коде мы обращаемся к ним с помощью переменных:
1 | const icon = $('#icon'), iconHeader = icon.next(); |
Смотрим на работоспособность:
Изучаем разметку и код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <fieldset> <legend>Выбор картинки</legend> <div id="picHolder"> <img src="img/icon_video_thumb.png" alt="Icon Video" id="icon" title="Icon Video"> <h3>Video</h3> </div> <form id="picForm" name="picForm"> <input type="radio" name="picRadio" id="picRadio1" value="img/icon_video_thumb.png" checked> <label for="picRadio1">Video</label> <input type="radio" name="picRadio" id="picRadio2" value="img/icon_blueprint_thumb.png"> <label for="picRadio2">Blueprint</label> <input type="radio" name="picRadio" id="picRadio3" value="img/icon_realtor_thumb.png"> <label for="picRadio3">Realtor</label> <input type="radio" name="picRadio" id="picRadio4" value="img/icon_medicine_thumb.png"> <label for="picRadio4">Medicine</label> <input type="radio" name="picRadio" id="picRadio5" value="img/icon_swish_thumb.png"> <label for="picRadio5">Swish</label> <select name="selPic" id="selPic"> <option value="img/icon_video_thumb.png" selected>Video</option> <option value="img/icon_blueprint_thumb.png">Blueprint</option> <option value="img/icon_realtor_thumb.png">Realtor</option> <option value="img/icon_medicine_thumb.png">Medicine</option> <option value="img/icon_swish_thumb.png">Swish</option> </select> </form> </fieldset> <script> $(function(){ const icon = $('#icon'), iconHeader = icon.next(); $("#picForm").on('change', 'input, select', function() { let text = $(this).next().text() || $(this).children('option:selected').text(); icon.attr({ src: $(this).val(), alt: text, title: "Icon "+text }); iconHeader.text(text); }); }) </script> |
Для того чтобы понять, как действует код, давайте посмотрим на скриншот. На нем видно, где какие переменные и функции, которые существуют в коде.
Возможно, у вас вызвала вопрос строка:
1 | let text = $(this).next().text() || $(this).children('option:selected').text(); |
Здесь в переменную text
попадает либо текст, который записан между тегами <label></label>
, находящимися за выбранной radio-кнопкой, либо между тегами <option></option>
в выбранном пункте выпадающего списка <select>
. В том случае, если вы выбираете какой-либо пункт из выпадающего списка, $(this).next().text()
вернет пустую строку, что идентично false
в JavaScript/jQuery, и в переменную text
запишется значение, определяемое строкой $(this).children('option:selected').text()
.
Особенности использования делегирования событий
Когда вы делегируете обработку событий от родительского элемента дочерним, следует учитывать то, что чем выше в иерархии дерева DOM будет находится родитель (body
, например), тем чаще будет вызываться для него обработчик события, т.к. он будет ловить события, всплывшие от большего количества элементов. Причем при каждом таком вызове он будет производить проверку, а является ли источник этого события нужным элементом. То есть делегирование может привести к избыточным нагрузкам, связанным с вычислениями. Поэтому желательно привязывать делегированные обработчики к непосредственным родителям интересующих вас элементов.
Такие события, как focus
и blur
по спецификации W3C не имеют особенности подниматься вверх по иерархии DOM, однако в jQuery организованы их альтернативы — focusin
и focusout
, которые умеют "всплывать". Поэтому, если вы попытаетесь установить делегированные обработчики focus
или blur
, то jQuery по факту будет отслеживать focusin
и focusout
. Поэтому лучше сразу использовать обработчики focusin
и focusout
.
Еще одним событием, которое не всплывает вверх по иерархии DOM, является событие load . В IE 8 и младше, события paste
и reset
так же не обладают этим свойством. Поэтому не стоит обрабатывать эти события делегированно. Привязывать обработчики этих событий следует непосредственно на элементы, на которых они происходят (без делегирования).
Добавление обработчика события методом bind() - устаревший способ
Этот метод существовал и использовался еще до появления метода on()
. На данный момент необходимость назначать обработчики событий методом bind()
является сомнительной, но пока еще доступной.
Синтаксис использования метода bind()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // одно событие $('селектор').bind('событие', function(){ ... }); // несколько событий $('селектор').bind('событие1 событие2', function(){ ... }); // несколько событий в нескольких функциях (с jQuery 1.4) $( "p" ).bind({ click: function() { // При клике }, mouseover: function() { // При наведении курсора мыши }, mouseout: function() { // При уведении курсора мыши } }); |
Если посмотреть внимательно, то все то же самое можно сделать (причем с версии jQuery 1.7) более современным методом on()
. В официальной документации написано , что начиная с jQuery 3.0, метод bind() устарел. Это объясняется тем, что метод on()
заменил собой такие методы, как bind()
, delegate()
и live()
, поэтому стоит использовать именно его. Тем не менее, в Интернете существует масса примеров, в которых метод bind()
используется и рекомендуется для присоединения обработчика событий к различным элементам.
Мы рассмотрим пример использования метода bind()
для обработки события focusout
для полей типа text
и password
формы. Однако еще раз подчеркнем, что его стоит заменить методом on()
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <form name="login-example"> <p><label for="user-login">Your login </label> <input type="text" name="user-login" id="user-login" required autocomplete="off"> </p> <p><label for="user-pass">Your password </label> <input type="password" name="user-pass" id="user-pass" required> </p> </form> <script> $(function(){ $(':text, :password').bind('focusout', function() { if ($(this).val().length < 5) { alert("Введите не менее 5 символов"); return false; } }) }); </script> |
Введите 1-4 символа в каждое из полей ввода и нажмите клавишу Tab
- и вы увидите сообщение о том, что нужно не менее 5 сиволов.
Удаляем обработчики событий
Не очень часто, но все же бывает необходимо удалить обработчик события после того, как он выполнил свой код. В нативном JavaScript для того, чтобы удалить обработчик , нужно записать строку onсобытие=null
или использовать метод removeEventListener('событие', имя_функции)
, указав нужное событие и имя функции-обработчика.
В jQuery для удаления обработчика(-ов) событий служать методы off()
и устаревший уже на данный момент unbind()
. При минимальном знании английского языка вы можете найти зависимость между назначением и отменой обработчиков событий: on()
включает (добавляет) обработчик, а off()
, напротив выключает (отменяет) обработчик события; bind()
- привязывает обработчик события, unbind()
- отвязывает его.
Удаление обработчика события методом off()
Метод off()
удаляет обработчики событий, назначенные методом on()
и имеет подобный синтаксис.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //общий вид отмены обработчика события $('селектор').off('событие'); //для назначенного ранее события мыши $('селектор').off('mousemove'); //отмена всех обработчиков событий элемента $('селектор').off(); //отмена всех делегированных обработчиков событий клика для всех параграфов $( "p" ).off( "click", "**" ); //отмена нескольких обработчиков событий $('селектор').off('keydown input cut'); //отмена обработки события формы с делегированием в элементы input и select $('form').off('change', 'input, select'); //отмена обработки события клика с указанием вызываемой функции clickHandler $( "body" ).off( "click", "p", clickHandler); |
Например, нам нужно назначить и удалить обработчик события клика на заголовке, который разворачивает скрытый в css-стилях блок.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <style> .hidden {display: none;} #test-block h4 { background-color: #5b7bfd; padding: 10px; color: #fff; cursor: pointer; } </style> <div id="test-block"> <h4>What is "Lorem ipsum"?</h4> <p class="hidden">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quibusdam ... ia?</p> <h4>Why do we need "Lorem ipsum"?</h4> <p class="hidden">Sapiente explicabo aspernatur fugiat animi possimus ... voluptate dolore?</p> <h4>Where is "Lorem ipsum" using?</h4> <p class="hidden">Nobis fugit officiis quo ipsam facilis rem sit a nostrum ... onsequuntur non!</p> <h4>Is "Lorem ipsum" a real text?</h4> <p class="hidden">Sit ipsum exercitationem placeat, explicabo inventore reiciendis ... dantium nisi?</p> <button class="button" id="addEventHandler">Добавить обработчик события</button> <button class="button" id="removeEventHandler">Удалить обработчик события</button> <script> (function($) { $("#addEventHandler").on('click', function() { $('#test-block').on('click', 'h4', showInfo) }); $("#removeEventHandler").on('click', function() { $('#test-block').off('click', 'h4', showInfo) }); let showInfo = function() { console.log($(this).next()); $(this).next().slideToggle(400); } })(jQuery) </script> </div> |
Функция showInfo()
либо назначается для обработки клика по каждому из h4
внутри <div id="test-block">
, либо отменяется в зависимости от того, на какой кнопке под заголовками вы кликните. Обратите внимание, что изначально обработчик события для заголовков не задан, поэтому клики по ним ничего не дадут.
Чтобы развернуть блоки, кликните на кнопку, добавляющую обработчик события.
What is "Lorem ipsum"?
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quibusdam, accusamus, dolorum? Autem aliquam modi cumque sint quis, asperiores laudantium corporis expedita porro minus quasi harum, illum iste ducimus exercitationem consequuntur veritatis ab fugit, numquam quia?
Why do we need "Lorem ipsum"?
Sapiente explicabo aspernatur fugiat animi possimus nemo placeat, ipsam cupiditate voluptatum cumque dignissimos corporis, doloremque molestiae eum repellendus. Quae rem eveniet perspiciatis amet facere, aliquam maiores magni quasi ad esse deserunt, sequi, asperiores voluptate dolore?
Where is "Lorem ipsum" using?
Nobis fugit officiis quo ipsam facilis rem sit a nostrum eligendi praesentium deleniti quam iste dolores, reiciendis odio. Error nobis dolore dolorem molestias reprehenderit iusto soluta dolorum omnis vero eos, iure est tempora consequuntur non!
Is "Lorem ipsum" a real text?
Sit ipsum exercitationem placeat, explicabo inventore reiciendis nihil dolor quibusdam et deserunt modi consequuntur perspiciatis, rem laudantium eos adipisci molestiae facere enim sint. Doloribus quae quam voluptatem hic praesentium ut voluptate est fugiat laudantium nisi?
Давайте рассмотрим еще один пример, в котором при клике на кнопку мы отображаем картинку с фейерверком за счет изменения css-свойства visibility
и удаляем обработчик события клика, т.к. повторный клик не имеет смысла, поскольку картинку мы уже видим.
1 2 3 4 5 6 7 8 9 10 | <div class="for-fireworks"> <img src="img/fireworks.gif" alt="Fireworks" width="500" height="474"></div> <button class="button" id="want-firework">Хочу фейерверк</button> <script> $(document).ready(function() { $("#want-firework").on('click', function() { $(this).prev().find('img').css('visibility', 'visible'); $(this).off('click'); }); }) |
Попробуйте сами.
Если заглянуть в Инспектор свойств в Firefox (F12
), то можно увидеть, как изначально к кнопке привязан обработчик события (слово event
справа от закрывающего тега), а после клика на ней - уже его нет.
Такое же действие можно, т.е. однократное срабатывание события с последующим удалением обработчика этого события, можно сделать с помощью метода one().
Отмена обработчика события методом unbind() - устаревший способ
Начиная с jQuery 3.0, метод unbind()
считается устаревшим. Он был заменен методом off()
, начиная с jQuery 1.7, поэтому его использование на данный момент не поощряется, но пока остается доступным. Вы можете посмотреть на пример его использования выше для таблицы.
Разовое (однократное) срабатывание события с помощью обработчика one()
В том случае, если вам необходимо, чтобы событие сработало однократно и больше не отслеживалось, используйте специальный обработчик one()
. Его синтаксис таков:
1 2 3 4 5 6 7 | $('селектор').one('событие', function(){ //код }); //например, $('#my-elem').one('mousedown', function(){ alert('Произошло событие "mousedown"'); }); |
Рассмотрим пример со списком покупок. Изначально у вас ничего нет из тех покупок, за которыми вы идете в магазин, на рынок или в супермаркет. Но с течением времени вы вычеркиваете те пункты, которые соответствуют купленным продуктам. Делать это что с реальным, что с электронным списком достаточно один раз.
Разметка и код примера будут таковы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <style> .my-choice li { list-style-type: none; cursor: pointer; } .my-choice li:before { content: '\BB'; display: inline-block; margin-right: 7px; } .my-choice .choosen {color: #03af4c; text-decoration: line-through;} .my-choice .choosen:before { content: '\2713'; color: #03af4c; } </style> <ul class="my-choice"> <li>Картофель 2кг</li> <li>Молоко 1л</li> <li>Сыр российский 0,5кг</li> <li>Мороженое "Супершоколад" 3шт.</li> <li>Пепси-кола 0,5л</li> </ul> <script> $(function() { $(".my-choice li").one('click', function() { $(this).addClass('choosen'); }); }) </script> |
Большую часть работы здесь делае css. В коде мы просто добавляем класс 'choosen'
к элементам li
, на которых был выполнен клик.
Сам пример:
- Картофель 2кг
- Молоко 1л
- Сыр российский 0,5кг
- Мороженое "Супершоколад" 3шт.
- Пепси-кола 0,5л
Если заглянуть в Инспектор кода в браузере Firefox (он показывает, к каким элементам назначен обработчик события и какой), то можно увидеть такую картину:
В тех элементах li
, на которых уже сделали клик, обработчик события отсутствует - там пусто в том месте, где у остальных li
мы видим слово event
, обозначающее, что к элементу добавлен обработчик события. Если же мы кликнем на слове event
, то увидим, что там назначены 2 обработчика события click
, и во втором таком обработчике вызывается функция off()
, отменяющая обработку события.