16 November 2013

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

Website developmentJavaScript
Original author: Jeff Griffiths
DOM Mutation Events в свое время казались отличной идеей — веб-разработчики начали создавать более динамичные приложения, и казалась естественной та радость с которой были встречены новые возможности прослушивать изменения DOM и реагировать на них. На практике, однако, оказалось, что у DOM Mutation Events имеются серьезные проблемы с производительностью и стабильностью. Не удивительно, что спецификация через год получила статус “устаревшей”.

Но сама идея, лежащая в основе DOM Mutation Events казалась привлекательной и поэтому в сентябре 2011 г. группа инженеров Google и Mozilla представила предложение о DOM MutationObserver, с похожей функциональностью, но улучшенной производительностью. Это новое DOM-API доступно начиная с версий: Firefox 14, Chrome 18, IE 11, Safari 6 (остальные браузеры — caniuse.com/mutationobserver)

В простейшем случае 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 Mutation Events является эффективность. При наблюдении за узлом DOM, ваша функция обратного вызова не сработает до тех пор, пока изменения DOM полностью не завершатся. Когда же callback сработает, в него будет передан итоговый список изменений 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>


Для того, чтобы вы могли посмотреть код в деле, я разместил его на jsbin:

jsbin.com/ivamoh/53/edit

Если вы поигрались с кодом в песочнице, вы, наверное, отметили одну особенность: функция обратного вызова срабатывает только тогда, когда вы нажимаете “ввод” на каком-либо элементе списка — по существу, это из-за того, что ваши действия приводят к добавлению или удалению узла DOM. Это важнейшее отличие от других техник, таких как подписка на нажатие клавиш, или клики мышью. MutationObservers работает не так, как все эти техники — здесь срабатывание происходит только при изменении DOM, а не как реакция на события, инициируемые JS, или действиями пользователей.

Итак, а где же это использовать?

Не думаю, что все программисты тут же ни с того ни с сего, все побросают, и начнут встраивать MutationObservers в свой код. Вероятно больше всего данный API подойдет разработчикам JS-фреймворков, для реализации чего-то нового, или того, что не было раньше реализовано из-за проблем с производительностью. Так же данная техника пригодиться, если вы используете какой-либо фреймворк, изменяющий DOM, и вам необходимо реагировать на эти изменения (без использования костылей, типа setTimeout). Ну и последнее — это разработка браузерных расширений.

Ресурсы:


От переводчика: материалу больше года, но что-то уж больно мало на русском о MutationObserver — может кому пригодится.
Tags:mutationobserver
Hubs: Website development JavaScript
+31
53.2k 175
Comments 17