Еще раз о делегирование или как правильно использовать свои события

    В этом посте хочу еще раз подчеркнуть механизм работы делегирования и использование собственных событий, чтобы избежать ошибок с которыми мне пришлось столкнутся.

    Для начала вспомним, что же такое делегирование на следующем примере:

    <div id=«container»>
      <a href="" class=«edit»>Edit</a>
      <a href="" class=«delete»>Delete</a>
    </div>

    <script type=«text/javascript»>
      $('#container').click(function(event) {
        var elem = $(event.target);

        if (elem.hasClass('edit')) {
          console.info('Clicked to edit link');
        }
        else if (elem.hasClass('delete')) {
          console.info('Clicked to delete link');
        }

        return false;
      });
    </script>


    В примере есть две ссылки, по клику на которые выполнятся те или иные действия. В данном случае это редактирование и удаление item. Для того, чтоб не добавлять соответствующие слушатели на каждую из ссылок, можно просто добавить один слушатель для контейнера, в котором находятся ссылки и дальше уже по событию получить элемент с которым было связанно это событие. Т.е. в данном примере по клику на одну из ссылок мы получим элемент на который был произведен клик и определим какой из ссылок принадлежит это событие. Кликнув на ссылку 'Edit', в консоли выведется сообщение 'Clicked to edit link', а для ссылки ‘Delete’ — 'Clicked to delete link’.

    Делегирование очень хорошо повышает производительность приложение, так как вы не добавляете слушателя каждому элементу, а только контейнеру. Но мой пост немного не о том, это я решил напомнить, чтоб было понятно описанное ниже.

    В некоторых случаях, может потребоваться использовать структуры контейнеров с множеством вложений. Для простоты понимания, создадим структуру с одним вложением контейнеров:

    <div id=«container»>
        <div id=«subcontainer»></div>
    </div>

    В проекте, над которым я работаю, мне потребовалось использовать собственные события. Каждый из контейнеров имел свое событие, но оно именовалось одинаково для обоих. Но сам не подозревая того, я применил делегирование к структуре с вложенными контейнерами. Для наглядности, я вынес все в небольшой пример, который мы и рассмотрим.

    <div id=«container»>
        <div id=«subcontainer»></div>
    </div>

    <script type=«text/javascript»>
        $(document).ready(function() {
          $('#container').bind('some_event', function() {
            console.info('worked event for container');
          });

          $('#subcontainer').bind('some_event', function() {
            console.info('worked event for subcontainer');
          });

          $('#subcontainer').trigger('some_event');
        });
    </script>

    В этом примере мы просто добавляем слушателя родительскому контейнеру и отслеживаем событие 'some_event'. То же самое делаем и для вложенного контейнера 'subcontainer'. Если в данный момент вызвать триггер для вложенного контейнера, то в консоли у нас выведется:

    worked event for container
    worked event for subcontainer

    Если честно, когда я добавлял, у себя в проекте слушателей для одинаковых событий, к подобной структуре, то я такого результата не ожидал. События у меня именовались одинаково, да вот тока выполняли совсем разные действия. Когда код такой вот небольшой, то ошибку можно найти сразу, а если это все находится в классах по несколько 100 строк, то очень такие не просто отследить не правильный код. Чтоб избежать ошибки в выполнении, лучше всегда использовать namespaces.

    <script type=«text/javascript»>
        $(document).ready(function() {
          $('#container').bind('some_event.container', function() {
            console.info('worked event for container');
        });

          $('#subcontainer').bind('some_event.subcontainer', function() {
            console.info('worked event for subcontainer');
        });

          $('#subcontainer').trigger('some_event.subcontainer');
        });
    </script>

    Надеюсь этот пост поможет избежать ошибок в коде, с которыми мне пришлось столкнутся, при использовании собственных событий.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 28

      0
      Сэнкс. Намекни пожалуйста, когда лучше создавать *свои* события? Нутро чует что это полезно, но в голову не приходит.
      +3
      Можно trigger заменить на triggerHandler — он отменяет «bubbling», событие не будет передано родительским элементам
      docs.jquery.com/Events/triggerHandler
        0
        Спасибо, буду знать и о таком решении данной задачи.
        0
        Чтоб избежать ошибки в выполнении, лучше всегда использовать namespaces.
        А возвращение false в обработчике не поможет предотвратить выполнение обработчика в родителе?
          0
          Нет, так не будет работать, потому как функция callback будет вызвана в контексте функции trigger которая не возвращает результат работы callback'a. Да и нельзя такое делать. Представим ситуацию, когда вы добавляете несколько слушателей с одним и тем же событием для одного и того же элемента. Допустим 3 слушателя.

          $('#container').bind('some_event', function() {
            console.info('first');
            return false;
          });

          $('#container').bind('some_event', function() {
            console.info(‘second');
          });

          $('#container').bind('some_event', function() {
            console.info('three');
          });

          Если бы это работало, как вы говорите, то два callback’а которые добавлены двумя другими слушателями, просто бы не отработали, это породило бы множество ошибок.
            0
            Делать такое можно и return false отлично останавливает всплытие и обработку события браузером, например клик по линке. Кроме того это никак не повлияет на обработку события другими слушателями если такие имеются
              0
              В посте я рассматривал не событие «click», посмотрите пример выше. Вы немного не в ту сторону смотрите. Для данного примере, ваше утверждение несправедливо.
                0
                Справедливо. return false останавливает всплытие для любого (пользовательского или системного) события, но не отменяет выполнения других обработчиков для этого события.

                Т.е. утверждение
                Если бы это работало, как вы говорите, то два callback’а которые добавлены двумя другими слушателями, просто бы не отработали, это породило бы множество ошибок.

                неверно
                  0
                  Не понял, а причем здесь всплытие события, если в комменте обсуждали обработку этого события? я как раз и писал о слушателях события, и к этому и пример привел. Может я просто не так выразился и сбил вас столку.
                    +1
                    Постараюсь обьяснить позицию.
                    На коммент
                    А возвращение false в обработчике не поможет предотвратить выполнение обработчика в родителе?


                    Вы ответили см. habrahabr.ru/blogs/jquery/70760/#comment_2022846

                    И были неправы. Т.к. return false предотвращает обработку события в родителе (предовращает всплытие события), return false НЕ предотвращает обработку других слушателей этого же события. Т.е. весь коментарий неверен. Это я и пытался исправить.
                      0
                      Все спасибо, понял и проверил, действительно я ошибся, именно для родителя оно прекращает обработку. В споре рождается истина :) и в данном случае я для себя кое-что подчеркнул и это радует.
                        0
                        Всегда рад помочь в поисках истины )
          0
          namespace указывается после типа события, должно быть: some_event.container и some_event.subcontainer соответственно, как мне кажется.
          0
          Возможно, немного не в статье, но тоже про события… гугл мне ничем не помог…

          Как разделить в jQuery события Click и DblClick ??
          Т.е. если в скрипте отслеживается 2 разных события (и выполняются различные действия), то при событии DblClick всегда вначале отлавливается и исполняется одиночный клик, и только после этого срабатывает «двойной». Каким образом их можно разделить?
            0
            например проверить тип события о брабатывать нужный
            if(event.type=='click'){..}
              0
              поставил проверку

              $('#work').click(function(e){
              if (e.type=='dblclick'){alert(«TWO!»);}
              if (e.type=='click'){alert(«ONE!»);}

              Все равно ловит только «клик», до «Дабла» проверка не доходит… :(
                0
                А это потому что алерт вызывает модальное окно, и второй клик, который и должен был вызвать обработку dblclick обрабатывает алерт. Если поставить задержку на выполнение кода, то dblclick обработается.

                И конечно же нужно биндить оба события
                $('#work').bind('click',function(e){})
                  0

                  и
                    0
                    блин, сообщения сами коммитятся…

                    И конечно же нужно биндить оба события
                    $('#work').bind('click',function(e){})
                    $('#work').bind('dblclick',function(e){})

                    если прицепить таймер на клик, то по дблклику его нужно обнулять и код клика не будет обрабатыватся
                      0
                      погодите, но в js нет такого понятия как пауза (вроде как?!).

                      Или же в бинде на «клик» все завернуть в функцию и именно ее вызывать через setTimeOut ???

                      Или я не правильно Вас понял?

                      P.S. Прошу прощения если «туплю» :)
                        0
                        совершенно верно. а в дблклике очищать таймер.
                        примерно так:
                        var clickTimer;
                        $('#container').bind('click', function(){
                        clickTimer = setTimeout('alert(«Click»)', 200);
                        });
                        $('#container').bind('dblclick', function(){
                        clearTimeout(clickTimer);
                        alert('dblclick');
                        });
                          0
                          Огромное спасибо!
                          Поблагодарить плюсиком не хватает «заряда»… :(

                          Правда теперь столкнулся с проблемой передачи объекта вызываемой функции… т.е. у меня:

                          var clickTimer;
                          $('#work').dblclick(function(e){
                          clickTimer=1;
                          alert('dblclick');
                          });

                          //Обработка события CLICK в рабочей области
                          $('#work').click(function(e){
                          clickTimer=0;
                          CaseClick(e);
                          });

                          function CaseClick (e){
                          if (clickTimer==1){return false;}
                          var clicked = $(e.target);
                          if (clicked.attr(«class»)=='run') {
                          e.preventDefault();
                          … и т.д.

                          если пытаюсь вызвать СaseClick через
                          setTimeout(function(e){CaseClick(e)}, 200);

                          он теряет объект и показывает «в никуда»…
                          Погуглил — говорят, что это ограничение JS, как выход — объявление глобального объекта, но ИМХО — это как то криво… Может есть красивее решение, как его передать?
                          P.S. Вне зависимости, еще раз СПАСИБО!
                            0
                            решается с пом замыкания
                            на хабре было пару статей на эту тему

                            в Вашем случае будет примерно так:
                            setTimeout(
                            (function(event){
                            return function(){CaseClick(event)}
                            })(e)
                            , 200);

                              0
                              Заработало!
                              Еще раз ОГРОМНОЕ спасибо!

                              Вчера весь день на эти замыкания убил, с return видать не домудрил что то, вчера не работало никак. Сейчас тупо Вашу строчку скопипастил и все заработало как часики… :)
                                0
                                Пожалуйста. Но замыкания таки нужно освоить, открывает новые возможности в программирование на js.

                                P.S. И не верте тем кто говорит что у JS есть ограничения.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое