Как использовать плагины jquery с динамическим контентом или почему после AJAX отваливается javascript

1,00
р.
Подключил на страницу несколько суперплагинов:
$("*[data-foo]").foo() $(".bar").bar() $("p a b i").baz()
Но после загрузки через AJAX код перестает работать! Как это исправить?!

Ответ
Говорить, что "код перестает работать" - некорректно. Потому что код работать и не начинал. Когда вы пишите $(селектор).метод() - это означает однократный вызов метода. Этот метод применяется ко всем элементам, соответствующим переданному селектору, которые в этот момент были на странице.
Обычно говорят, что чтобы исправить ошибку, надо после обновления страницы через ajax выполнить код еще раз. Но это не всегда верно.
Повторное выполнение вызова вида $(".bar").bar() вызовет плагин bar не только на новых элементах - но и на старых. В лучшем случае будет бесполезно потраченное время. Но в худшем случае из-за повторного вызова что-нибудь поломается (а если вы подписывались на события - то что-нибудь поломается гарантировано!). Поэтому надо применять плагины только к обновленному контейнеру.
Также бывают грабли с тем, как вызывать эти плагины после обновления. Обычно это пытаются делать через инлайн script в ответе сервера - но в таком случае этот скрипт не имеет доступа к обновляемому элементу. Надо искать в коде где происходит само обновление - и править там.

Выше был общий принцип. Ниже я напишу способ, которым можно попытаться заставить заработать код в простом случае.
Собираем все "улучшения" страницы в одном месте: applyPlugins()
// ... function applyPlugins() { $("*[data-foo]").foo() $(".bar").bar() $("p a b i").baz() }
Изменяем получившуюся функцию так, чтобы она действовала только внутри переданного ей контейнера: applyPlugins($(document))
function applyPlugins($cnt) { $cnt.find("*[data-foo]").foo() $cnt.find(".bar").bar() $cnt.find("p a b i").baz() }
Здесь мы воспользовались методом find, который ищет дочерние элементы. Важно! Функция applyPlugins не должна обращаться к элементам за пределами переданного ей контейнера $cnt! Находим в коде те места, где выполняется динамическое обновление содержимого или создание элементов. Это может быть вызов .html(...): $.ajax({ // ...
success: function (data) { $("#some_block").html(data) } })
В таком случае надо после вызова html добавить вызов applyPlugins: $.ajax({ // ...
success: function (data) { applyPlugins($("#some_block").html(data)) } })
Это может быть вызов .load(): $("#some_block").load("http:/ome/url")
В таком случае надо добавить туда параметр с функцией обратного вызова: $("#some_block").load("http:/ome/url", function () { applyPlugins($(this)) })
Это может быть и что-то другое. Но все же надеюсь что вы это место найдете - это же ваш код, а не чей-то еще :)

Способ выше не будет работать если используемый для плагина селектор проходит через динамический контейнер:
$(".foo .bar .baz").somePlugin()
$(".bar").load(...)
В таком случае попытка прямого переписывания в applyPlugins в виде $cnt.find(".foo .bar .baz").somePlugin() будет неудачной, поскольку ни .foo, ни .bar не являются дочерними элементами для обновляемого контейнера.
В таком случае вам, наверное, стоит более внимательно отнестись к тому что и как вы обновляете или загружаете вместо слепого применения трюка с applyPlugins.
Или же можно попытаться сделать как-то так, чтобы предусмотреть все случаи - но этот код для понимания и для отладки будет довольно тяжелым:
$cnt.find(".foo .bar .baz").somePlugin() $cnt.filter(".foo, .foo *").find(".bar .baz").somePlugin() $cnt.filter(".foo .bar, .foo .bar *").find(".baz").somePlugin()
(Здесь проверяется, является ли текущий контейнер элементом .foo или его дочерним элементом, и если является - в пути .foo .bar .baz пропускается первый элемент. Этот код предполагает, что один .foo не может быть вложен в другой. То же самое для .bar.)

PS хотя я говорил только про плагины, ответ можно применять и для подписки на события. Но это будет не самый лучший способ - потому что есть способ проще. Большинство событий является всплывающими - а потому их можно отлавливать в корне документа. И JQuery имеет встроенные механизмы для фильтрации всплывших событий. Пример:
$(document).on("click", "a[data-href]", function (e) { // ... })
Этот обработчик будет слушать нажатия на любые ссылки с установленным атрибутом data-href независимо от того, как и когда они появились на странице.