Как использовать плагины 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 независимо от того, как и когда они появились на странице.