На написание этой статьи меня подвигло то обстоятельство, что на один и тот же вопрос приходится отвечать по 5-7 раз в неделю. Вопрос этот обычно звучит примерно так: «Элементы вставляются в страницу из ajax-запроса к серверу и для них не работает ни один обработчик событий! Как это поправить?».
Начнем пожалуй с того, что поправлять вряд ли что-то нужно. Скорее надо разобраться с тем, что, и в какой последовательности происходит. Разберемся на вот таком простом примере. Я приведу HTML, javascript-код и php-код сценария на сервере.

<button>Отправить ajax-запрос</button>
<span id="process"></span>
<p>Параграф 1</p>
<p>Параграф 2</p>

HTML-код очень прост. Имеем два параграфа, кнопку, с помощью которой будем отправлять ajax-запрос и элемент span, который будет использоваться для вставки в него текстовых сообщений.

Теперь javascript-код:

$(document).ready(function(){
  // обрабатываем клик на параграфах
  $("p").click(function() {
  	alert($(this).text());
  });
  // ajax-запрос
  $("button").click(function() {
    $.ajax({
     url: "eventHandlingAjax.php",
     beforeSend: function(){
       $("#process").css("display","inline")
                    .text("Отправляю ajax-запрос");
     },
     success: function(answ){
       $("#process").text("Ответ получен")
                    .fadeOut(3000);
       $("body").append(answ);
     }
    });
  });
});

Для начала свяжем событие click с элементами p. По событию будем выводить в alert текст, содержащийся в данном параграфе.

Ajax-запрос будем выполнять при клике на элементе button. Сложного тут ничего нет – запрос будет отправлен к файлу eventHandlingAjax.php, а перед отправкой мы вставим соответствующее сообщение в элемент span, сделав его видимым с помощью CSS (изначально этот элемент был скрыт с помощью того же CSS). При получении ответа от сервера мы вставим сообщение об этом в тот же элемент span и самое главное, добавим в body то, что мы получили в качестве ответа.

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

<?php
header('Content-Type: text/html; charset=utf-8');
if($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
    print '<p>Добавленный параграф</p>;
}
?>

Мы добавим в body всего-навсего HTML-код еще одного параграфа.

Если Вы уже успели испытать этот пример, то наверняка отметили то обстоятельство, что обработчик события click действует только для тех двух параграфов, которые присутствовали на странице с самого начала. А для добавленных параграфов этот обработчик не действует. Почему? Давайте разбираться.

Что происходит в момент загрузки HTML-страницы? На экране монитора появляется кнопка и два параграфа. Самое интересное в javascript-коде. В момент готовности DOM наш javascript-код начинает отрабатывать, и первое, что он делает, отбирает в объект jQuery все элементы p на странице и вешает на них обработчик события click. Затем уже вешает обработчик клика и на элемент button, но это нас сейчас интересует в гораздо меньшей степени.

Что происходит дальше? Дальше мы отправляем ajax-запрос к серверу и получаем от него в качестве ответа еще один элемент p, который вставляется в DOM. Вот это и есть самый важный момент – обработчик события click по сути связан не с какими-то элементами p на странице, а с конкретным объектом jQuery, в котором на данный момент находится только два элемента p, которые присутствовали на странице изначально. Добавленный параграф в этот объект jQuery не мог быть включен, поскольку тогда еще не существовал, поэтому и обработчик события click для него работать не будет.

Ну а теперь узнаем «Как это поправить?»

Поправить это можно, добавив обработчик события клик в callback-функцию, определенную в опции success ajax-запроса. Я приведу javascript-код и Вы сможете посмотреть, как он изменился.

$(document).ready(function(){
  // обрабатываем клик на параграфах
  $("p").click(function() {
  	alert($(this).text());
  });
  // ajax-запрос
  $("button").click(function() {
  	$.ajax({
     url: "eventHandlingAjax.php",
     beforeSend: function(){
       $("#process").css("display","inline")
                    .text("Отправляю ajax-запрос");
     },
     success: function(answ){
       $("#process").text("Ответ получен")
                    .fadeOut(3000);
       $("body").append(answ);
       $("p").click(function() {
         alert($(this).text());
       });
     }
    });
  });
});

Я специально не стал удалять первый обработчик события click, поскольку на этом примере можно наглядно увидеть разницу между двумя объектами jQuery, которые содержат элементы p. Первый из них был создан в момент загрузки html-страницы и содержит в себе два элемента p, а вот второй объект jQuery создан уже после выполнения ajax-запроса, и содержит все элементы p, которые удалось обнаружить в DOM, т.е. после одного запроса будет три параграфа, после двух – четыре и так далее. Визуально существование двух объектов jQuery подтверждается тем, что при клике на любом из «добавленных параграфов» Вы увидете только одно сообщение в alert’е, а вот при клике на первом или втором параграфе Вы увидете уже два alert’а – соответственно для первого и для второго объекта jQuery. Количество объектов jQuery будет расти вместе с количеством выполненных ajax-запросов.

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

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

Я хочу рассказать о еще одном возможном способе решения этой задачи, который состоит в использовании плагина jQuery Listen. В коде кое-что конечно изменится. Естественно, что для использования этого плагина, его необходимо подключить к странице:

<script type="text/javascript" src="js/jquery-1.2.6.js"></script>
<script type="text/javascript" src="js/jquery.listen.js"></script>

HTML-код остался без изменений, а вот javascript-код изменился:

$(document).ready(function(){

  jQuery.listen("click", "p", function(){
    alert($(this).text());
  });

  // ajax-запрос
  $("button").click(function() {
  	$.ajax({
     url: "eventHandlingAjax.php",
     beforeSend: function(){
       $("#process").css("display","inline")
                    .text("Отправляю ajax-запрос");
     },
     success: function(answ){
       $("#process").text("Ответ получен")
                    .fadeOut(3000);
       $("body").append(answ);
     }
    });
  });
});

Обратите внимание, что вместо нашего обработчика события click на элементах p, мы используем плагин jQuery Listen, передавая ему событие, которое необходимо отслеживать, элементы, с которыми это событие необходимо связать и функцию, которая будет выполнена при наступлении этого события. Можете посмотреть работу плагина – вот пример.

google.com bobrdobr.ru del.icio.us technorati.com linkstore.ru news2.ru rumarkz.ru memori.ru moemesto.ru