вторник, 28 ноября 2017 г.

DOM MutationObserver – реакция на изменение DOM модели без потери производительности браузера.

События изменения DOM модели казались отличной идеей в свое время – разработчики могли создавать более динамические сценарии, казалось вполне естественным иметь возможность слушать изменения в DOM и реагировать на них. На практике использование данного API вылилось в большую проблему производительности и стабильности, поэтому с недавних пор события изменения DOM модели стали помечеными как устаревшие.
Оригинальная идея по возможности отслеживания изменений по прежнему осталась привлекательной, поэтому в сентябре 2011 года группа инженеров Google и Mozzilla представила новое решение, которое предоставляет похожую функциональность но показывает куда более высокую производительность: DOM MutationObserver. Этот новый DOM API доступен на большинстве современных браузерах включая IE11+, Edge, Firefox, Chrome.
В самом простейшем случае, реализация MutationObserver выглядить примерно так:

// выбираем текущий элемент
var target = document.querySelector('#some-id');

// создаем экземпляр наблюдающего объекта
var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        console.log(mutation.type);
    });
});

// конфигурируем его:
var config = { attributes: true, childList: true, characterData: true }

// передаем элемент и конфигурацию в метод запуска наблюдения
observer.observe(target, config);

// позже процесс наблюдения можно остановить
observer.disconnect();

Ключевым преимуществом новой спецификации по сравенению со старыми событиями DOM является эффективность. Если вы наблюдаете за изменениями элемента, вункция возврата не будет вызвана до тех пор, пока DOM не закончит изменения. В момент вызовы функции возврата, уже имеется информация о перечне изменений в DOM, эту информацию вы можете использовать для того что бы среагировать так как вам нужно.
Это также означает, что любой код который вы напишете должен обрабатывать результат наблюдений в том порядке, в котором происходят изменения. Ниже представлен компактный пример налюдателя который слушает изменения в редактируемом упорядоченном списке.

<!DOCTYPE html>
<ol contenteditable oninput="">
  <li>Press enter</li>
</ol>
<script>
  var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
  var list = document.querySelector('ol');

  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      if (mutation.type === 'childList') {
        var list_values = [].slice.call(list.children)
            .map( function(node) { return node.innerHTML; })
            .filter( function(s) {
              if (s === '<br />') {
                return false;
              }
              else {
                return true;
              }
        });
        console.log(list_values);
      }
    });
  });

  observer.observe(list, {
        attributes: true,
        childList: true,
        characterData: true
   });
</script>



Если вы хотите посмотреть как работает этот код, то вы можете воспользоваться ссылкой, представленной ниже.

Если вы посмотрите на этот пример, то можете обратить внимание на то что функция возврата срабатывает каждый раз когда вы жмете на Enter для каждого li, на практике когда пользователь что то делает, то результат добавляется или удаляется из DOM. Это важное отличие от других приемов, таких как подключение событий на нажатие кнопки или более глобальное событие такое как ‘click’. MutationObserver работает отлично от таких приемов, потому что его вызовы инициируются самими изменениями DOM, а не событиями которые генерируются либо JS либо активностью пользователя.

Так для кого это хорошо?
Я не думаю что большинство JS хакеров сейчас отложат свои дела и начнут активно добавлять mutation observer в свой код. Пожалуй самая большая аудитория для этого нового API это люди которые пишут фреймворки, в большей части для решения проблем и создания взаимодействия, которого раньше они не могли написать, или по крайней мере с приемлимой производительностью. Другой сценарий – это если вы используете некоторый фреймворк для изменения DOM и хотите реагировать на изменения более эффективно (и без всяких вызовов setTimeout).

Что еще почитать


Источник: https://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-to-dom-changes-without-killing-browser-performance/