Сделать вызов AJAX через динамически вставленный элемент DOM, нужно ли мне использовать наблюдатель мутаций?

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

Простой способ реализовать это — использовать класс (скажем, .ajax-loader) и выполнить цикл по каждому из них и загрузить данные AJAX из data- атрибутов самого элемента.

Что-то вроде этого (jsfiddle):

<div class="ajax-loader" data-url="/echo/html/" data-html="<p>hello world</p>" data-delay="1">Loading...</div>

<script>
  $('.ajax-loader').each(function() {
    var ele = $(this);
    var url = ele.data('url');
    var html = ele.data('html');
    var delay = ele.data('delay');

    return $.post({
      url: url,
      data: {
        html: html,
        delay: delay
      }
    }).done(function(d) {
      ele.html(d);
    });
  });
</script>

Пока это работает нормально, однако проблема возникает, когда мне нужно динамически вставить эти элементы .ajax-loader на страницу. Моя функция .each() запускается только один раз и не запускается снова, если на страницу вставляется новый элемент .ajax-loader (вы можете увидеть это в предыдущем jsfiddle, нажав кнопку).

Поэтому я добавил наблюдатель мутаций для обнаружения изменений DOM. Теперь мой код выглядит так (jsfiddle):

<button class="insert-ele">Click to insert new AJAX object</button>
<div id="container">
  <div class="ajax-loader" data-url="/echo/html/" data-html="<p>Hello World!</p>" data-delay="1">Loading...</div>
</div>

<script>
$(document).ready(function() {
  // Insert new ".ajax-loader" objects on button click
  $('.insert-ele').on('click', function(e) {
    $('#container').append('<div class="ajax-loader" data-url="/echo/html/" data-html="<p>I was inserted via JS.</p>" data-delay="1">Loading...</div>');
    e.preventDefault();
  });

  // Define our ajaxLoader function
  var ajaxLoader = function() {
    var ele = $(this);
    ele.addClass('ajax-load-started');

    var url = ele.data('url');
    var html = ele.data('html');
    var delay = ele.data('delay');

    return $.post({
        url: url,
      data: {
        html: html,
        delay: delay
      }
    }).done(function(d) {
        ele.html(d);
      ele.addClass('ajax-load-completed');
      ele.removeClass('ajax-load-started');
    });
  };

  // Loop through each .ajax-loader, and load their AJAX request.
  $('.ajax-loader:not(.ajax-load-completed):not(.ajax-load-started)').each(ajaxLoader);

  // Observe DOM changes, and load AJAX data when it does change
  var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
  var observer = new MutationObserver(function(mutations) {
    $('.ajax-loader:not(.ajax-load-completed):not(.ajax-load-started)').each(ajaxLoader);
  });

  // Activate the observer
  var container = document.getElementById('container');
  observer.observe(container, {
    attributes: true, 
    childList: true, 
    subtree: true
  });
});
</script>

Однако, глядя на Совместимость браузера для Mutation Observers, я видите, он поддерживается только в IE 11. Мне нужно поддерживать IE 9 и, возможно, IE 8, хотя это не так важно.

Есть ли лучший способ сделать это? Или просто другой способ, который даст мне лучшую совместимость с браузером?

Я также понимаю, что могу запустить функцию $('.ajax-loader').each() в обработчике $('.insert-ele').on('click'), но я бы также хотел избежать этого решения, если это возможно.


person romellem    schedule 29.02.2016    source источник
comment
Так почему бы вам просто не запустить событие, когда вы добавляете элементы, и не прослушать это событие, чтобы запустить каждый цикл?   -  person epascarello    schedule 29.02.2016
comment
Я мог бы это сделать, но в моем текущем приложении у меня не всегда есть красивое/простое действие, которое запускает вставку .ajax-loader (например, нажатие кнопки). Способ вставки этих элементов зависит от приложения.   -  person romellem    schedule 29.02.2016
comment
Тот факт, что существуют сложные и продвинутые методы и объекты, не означает, что вы должны использовать их, когда под рукой есть более простые решения.   -  person Ason    schedule 29.02.2016
comment
Разве у вас нет общей точки, где они добавляются?   -  person epascarello    schedule 29.02.2016
comment
Также можно использовать глобальные методы, такие как $.ajaxComplete.   -  person charlietfl    schedule 29.02.2016
comment
@epascarello, на самом деле, чем больше я об этом думаю, тем больше общего. Я не решался решать проблему таким образом, поскольку эти общие моменты разбросаны по (существующей) кодовой базе. Это просто означает, что мне нужно будет добавить больше кода и убедиться, что я попал во все необходимые места. Вот почему мне нужен универсальный подход, который я мог бы вставить на страницу, и это помогло бы на всех моих страницах.   -  person romellem    schedule 29.02.2016


Ответы (1)


Наблюдение за мутациями здесь не очень подходит (независимо от того, как вы это реализуете): проверка изменений в DOM (или любом объекте) довольно затратна, так как требует сохранения копии объекта и повторения всего вещь, возвращаясь к свойствам объекта, чтобы проверить различия. В вашем случае вам не нужно знать о каких-либо изменениях, вы хотите знать, когда произошло конкретное событие (элементы dom добавлены на страницу).

Вы правы, когда хотите универсальное решение, и его не должно быть слишком сложно реализовать. Где-то у вас есть обратный вызов AJAX, отвечающий за добавление этих элементов на страницу. (Если у вас есть более одной функции, отвечающей за это, сейчас самое время объединить их в одну.) Когда эта функция обратного вызова AJAX вставила элементы DOM, сделайте так, чтобы она а) запускала обратный вызов, который вы определяете, или б) запускала пользовательское событие (вероятно, через jQuery, но Backbone и другие библиотеки имеют пользовательские утилиты событий), которые ваш другой код может прослушивать. Как бы вы ни подошли к этому, вы хотите, чтобы функция, вставляющая элементы DOM, делала что-то, что позволяло бы другому коду реагировать на действие.

Если код, ответственный за вставку элементов DOM на страницу, является сторонним, это может быть сложнее. (И самое время подумать, не лучше ли вам написать свою собственную реализацию.) Посмотрите на исходный код этого кода и дважды проверьте, не поддерживает ли он обратный вызов или событие или что-то, что позволяет вам интегрироваться с ним. Если нет, вы все равно сможете ответить на вставку DOM, прослушивая событие load.

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

person Brendan Gannon    schedule 29.02.2016