Страницы

Поиск по вопросам

понедельник, 8 октября 2018 г.

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

Подключил на страницу несколько суперплагинов:
$("*[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://some/url");
В таком случае надо добавить туда параметр с функцией обратного вызова:
$("#some_block").load("http://some/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 независимо от того, как и когда они появились на странице.

Комментариев нет:

Отправить комментарий