2015-04-29 5 views
0

У меня есть две мобильные страницы jquery, каждая в отдельном HTML-документе. Когда я ссылаюсь на второй документ, событие pagecreate запускается каждый раз. Это вызывает раздражающий побочный эффект при инициализации обработчиков несколько раз - один раз за каждый раз, когда страница была создана.JQuery mobile создает несколько обработчиков событий

Однако, я заметил, что есть разница между -

$ ('#' БТН) на ("нажать", функция() {...}).

и

$ (документ) .он ("щелчок", "#btn", функция() {...});

В первом случае обработчик события вызывается только один раз (это то, что я хочу), но во втором случае он вызывается один раз каждый раз, когда обработчик инициализирован. Может кто-нибудь объяснить, почему это так, и безопасно ли полагаться на первый случай, чтобы обработчик вызывался только один раз?

Вот начальная страница:

<body> 
    <div id="firstpage" data-role="page"> 
     <a href="testpageinit_2.php" id="goto" class="ui-btn">Page 2</a> 
    </div> 
</body> 

и вторая страница:

<body> 
    <div id="secondpage" data-role="page"> 
     <a href="#" id="btn" class="ui-btn">Click Me</a> 
     <a href="firstpage" id="btnback" class="ui-btn" data-rel="back">Back</a> 
    </div> 
</body> 

Мои JS выглядит следующим образом:

$(document).on("pagecreate", "#firstpage", function() { 
    alert("First page created"); 
}); 

$(document).on("pagecreate", "#secondpage", function() { 
    alert("Second page created"); 

    // This results in just one handler 
    $('#btn').on("click", function() { 
     alert("btn clicked handler"); 
}); 

    // This results in a handler being attached every time the page is created 
    $(document).on("click", "#btn", function() { 
     alert("document clicked handler"); 
    }); 
}); 

ответ

0

Посмотрите на документацию JQuery это к .on() способ, найден here. Там вы можете прочитать все о делегированных обработчиках событий и для чего они предназначены.

Чтобы обобщить документацию, делегированные обработчики событий свяжутся с элементом предка элемента, который вы действительно хотите прослушать для события. Затем делегированный обработчик полагается на распространение (или барботирование) для запуска обработчика, когда событие достигает предка, к которому он привязан, в вашем случае он ожидает, что событие достигнет $(document).

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

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


Пример

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

<body> 
    <div class="container"> 
     <ul class="my-list"> 
      <li>Some item 1</li> 
      <li>Some item 2</li> 
     </ul> 
    </div> 
</body> 

Представьте, что я хочу, чтобы изменить цвет фона любого .my-list > li элемента при щелчке.Для этого я использую следующий код:

$(".my-list > li").on("click", function() { 
    $(this).toggleClass("alternate-background"); 
}); 

Пока все будет работать так, как вы ожидали. Однако давайте теперь предположим, что у меня есть AJAX, который я использую для периодического пинга сервера и запроса дополнительных элементов .my-list > li. Наша новая разметка выглядит примерно так после первого возвращения запроса:

<body> 
    <div class="container"> 
     <ul class="my-list"> 
      <li>Some item 1</li> <!-- Loaded on page load --> 
      <li>Some item 2</li> <!-- Loaded on page load --> 
      <li>Some item 3</li> <!-- Loaded from AJAX --> 
      <li>Some item 4</li> <!-- Loaded from AJAX --> 
     </ul> 
    </div> 
</body> 

Если это наш вариант использования и мы по-прежнему использовать тот же клик-обработчик, как мы делали раньше, то мы будем замечать проблему : только фоны элементов .my-list > li, которые были загружены при загрузке страницы, будут меняться при щелчке элемента; элементы, добавленные через AJAX, не будут связаны с ними обработчиком.

Однако, если мы изменим наш клик обработчик к следующему, то фон каждого .my-list > li элемента (будь они добавили на странице загрузки или с помощью AJAX) изменится, когда указанный элемент щелкнул:

$(".container").on("click", ".my-list > li" function() { 
    $(this).toggleClass("alternate-background"); 
}); 

Примечание что в приведенном выше примере мы все еще используем $(this) для ссылки на элемент с нажатой кнопкой .my-list > li. К счастью, jQuery достаточно умен, чтобы знать, что мы хотим использовать тот элемент, который мы слушаем, а не тот, который мы слушаем, как элемент контекста.

+0

Большое спасибо за очень полный ответ. Я понимаю, что в первом случае у меня есть прямой обработчик, а во втором случае у меня есть делегированный обработчик, но я не понимаю, почему прямой обработчик вызывается только один раз, хотя он инициализируется несколько раз (в отличие от делегированный обработчик)? И могу ли я полагаться на это поведение? – engeeaitch

+0

Когда вы говорите «он инициализирован несколько раз», что вы имеете в виду? Из того, что я вижу в вашем коде, ваш прямой обработчик связан только один раз.Тур делегированный обработчик также связан только один раз, но он связан с неопределимым числом элементов, потому что он также может быть связан с элементами в будущем. Таким образом, на следующем 'pagecreate' новый прямой обработчик будет привязан к новому элементу' # btn', тогда как старый прямой обработчик будет привязан только к старому элементу '# btn'. Однако как исходные, так и новые делегированные обработчики будут привязаны как к старым, так и к новым элементам '# btn'. –

+0

Поскольку событие pagecreate для второй страницы срабатывает каждый раз, когда загружается вторая страница, обработчики событий связаны несколько раз, когда вы перемещаетесь между первой и второй страницами. Однако я заметил, что, хотя прямой обработчик связан несколько раз, когда происходит событие, обработчик вызывается только один раз. Напротив, делегированный обработчик вызывается несколько раз. – engeeaitch

0

Есть несколько более эффективные решения для множественного связывания задачи:

  1. Do Databinding на «pageinit» события вместо «pagecreate». 'pageinit' запускается только один раз.
  2. Перед добавлением новой привязки всегда удаляйте существующее связывание данных.

    $(document).off('click', '#btn').on('click', '#btn', function() { ... }

Первый метод работает для вас, потому что первый раз он пытается не связываться ни одного элемента пока существует на DOM с идентификатором «BTN», поэтому связывание не удается. Во втором методе вы используете то, что jquery вызывает «делегированное привязку события», поэтому привязка работает, даже если элемент «btn» добавлен в документ после привязки. Вы можете узнать больше об этом here. Для получения дополнительной информации о том, как все это работает, вы должны проверить this гораздо дольше, гораздо более подробный ответ.

+0

Я читал, что pageinit устарел в пользу pagecreate. Кроме того, я считаю, что элемент #btn должен существовать во время привязки - потому что обработчик успешно вызван после того, как страница была создана в первый раз, а также когда она будет создана впоследствии. – engeeaitch

0

Благодаря помощи от @Zachary, я думаю, что сейчас понимаю: Когда пользователь переходит на первую страницу, вторая страница выгружается вместе с любыми обработчиками, привязанными к элементам на этой странице (непосредственно связанный элемент в моем случае). Но обработчик, связанный с документом, остается.

Когда пользователь переходит на вторую страницу, обработчики снова связаны, что означает, что к документу прикреплены два обработчика, но только один обработчик, прикрепленный к #btn. Чтобы добавить вес к этой теории, я изменил делегированный обработчик, чтобы привязать его к div на второй странице, а затем он запускался только один раз.

Смежные вопросы