Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can MutationObserver make changes just right before mutation?

I need to prevent DOM-Change using mutationobserver.

I had (past) the following code to prevent specific changes:

document.bind("DOMSubtreeModified", function() {
   document.find('.Xx.xJ:Contains("wham")').closest("[jsmodel='XNmfOc']").hide();
});

Because of performance reasons I did not want to check the complete document on any dom-change but only added contents so I changed to this (now):

 var observer = new MutationObserver(function (mutations) {
        mutations.forEach(function (mutation) {
            [].slice.call(mutation.addedNodes).forEach(function (addedNode) {
               StartFilter(addedNode);                    

            });
        });
    });

    observer.observe(document, {
        childList: true,
        subtree:true,
        characterData:true,
        attributes:true        
    });

function StartFilter(newNode) {
   $(newNode).find('.Xx.xJ:Contains("wham")').closest("[jsmodel='XNmfOc']").hide();
}

But this does not really work. My guess is that "newNode" is not really a reference to the DOM-Element. (The selector is valid, "$(newNode).find('.Xx.xJ:Contains("wham")').closest("[jsmodel='XNmfOc']")" returns an element).

I did not find any method/property to reject a dom-change in MutationObserver. Is there a way to achieve what I want WITHOUT checking the whole document each time?

like image 272
Ole Albers Avatar asked Jan 07 '15 22:01

Ole Albers


1 Answers

It's unclear to me whether the NodeList returned by a mutation observer is "live", in the sense that changes to nodes in that list are immediately be reflected on the DOM. But that doesn't matter, since you're only using it to create jQuery wrapped set. The basic code you've got above works as intended (see simplified snippet below), which implies that there's something else preventing your hide() call from working as expected.

My best understanding is that you can't intercept and prevent changes to the DOM--the MutationObserver is fired after the associated mutation has already occurred. That means you're not interrupting or intercepting the mutation, but rather reacting to it. In your case, that could lead to unexpected "flashing" behavior as nodes are added and then removed. A better solution in that case would be to style newly-added nodes to be hidden by default, and then add a class/style to either display them or remove them from the DOM in the mutation observer filter.

var container = document.querySelector('.container');
var addNodeButton = document.querySelector('#add');
var addNodeWithHideButton = document.querySelector('#addWithHide');

var makeAddNode = function(includeHide) {
  return function() {
    var p = document.createElement('p');
    var s = document.createElement('span');
    var msg = 'Appended at ' + new Date().getTime();
    if (includeHide) {
      msg += ' (hide)';
    }
    var t = document.createTextNode(msg);
    s.appendChild(t);
    p.appendChild(s);
    container.appendChild(p);
    console.log('appended::', p);
  };
};

var makeNode = makeAddNode(false);
var makeNodeWithHidden = makeAddNode(true);

addNodeButton.addEventListener('click', makeNode);
addNodeWithHideButton.addEventListener('click', makeNodeWithHidden);

var toArray = function() {
  return [].slice.call(arguments);
};

var observer = new MutationObserver(function (mutations) {
  mutations.forEach(function (mutation) {
    toArray(mutation.addedNodes).forEach(function (addedNode) {
      StartFilter(addedNode);
    });
  });
});

observer.observe(document, {
  childList: true,
  subtree:true,
  characterData:true,
  attributes:true
});

function StartFilter(newNode) {
  var $n = $(newNode);
  console.log('$n::', $n);
  $n.find('span:contains(hide)').fadeOut(1500);
  //   $(newNode).find('.Xx.xJ:Contains("wham")').closest("[jsmodel='XNmfOc']").hide();
}
.container {
  width: 80%;
  border: 1px solid green;
  padding: 1rem;
  margin: 1rem auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button id="add">Add node without "hide"</button>
<button id="addWithHide">Add node with "hide"</button>
<div class="container"></div>
like image 53
Palpatim Avatar answered Sep 30 '22 02:09

Palpatim