javascript – Tag content change event

Question:

Is there an event in JavaScript that fires when the content of a tag changes? For example, when changing the content of <div></div>

Answer:

Introduction


There was an attempt to make events for changes in elements in the DOM, but it surfaced, it really greatly affected performance. They are called Mutation events in the literature:

  1. DOMAttrModified – if the attribute of the DOM element has changed;
  2. DOMAttributeNameChanged – if the name of the attribute on the DOM element has changed;
  3. DOMCharacterDataModified – if any text has changed, be it a regular 'text node' or a simple comment on a DOM element;
  4. DOMElementNameChanged – if the name of the DOM element has changed;
  5. DOMNodeInserted – if the DOM element has a new element inserted;
  6. DOMNodeInsertedIntoDocument – if a new element was inserted into the document ;
  7. DOMNodeRemoved – if any element has been removed from the DOM element;
  8. DOMNodeRemovedFromDocument – if any element in document been deleted;
  9. DOMSubtreeModified – if there has been any change to document .

They are all 'deprecated' and are highly discouraged. Moreover, it is far from a fact that they will work in your browser, and checking for this event is not such a trivial task, since elements with the same name do not have attributes.

Alternative 0, for the attentive


As an alternative, let's accept the fact that you can always create your own events that pop up on changes. That is, you can do it like this:

  1. come up with a name for the event, for example, DOM:changed ;
  2. create handler functions and subscribe them to these events;
  3. find all functions that can potentially change the DOM;
  4. for any changes that interest you in the DOM in these functions, we call a custom event.

This method has amazing possibilities in managing notifications, you can always choose what triggers an event, filter them carefully and create a comfortable atmosphere for them to be called. But there is always the possibility of forgetting to add this to the code, you need to be careful and test everything thoroughly.

Alternative 1, simple


You can think of two more ways to do this.

The first, in the forehead, is not entirely correct, but it takes place to live on small data, then I will tell you about the correct one:

  1. take the reference .innerHTML from your div and save it anywhere, for example, in sessionStorage
  2. every n seconds we compare the current .innerHTML with the reference, if they are not equal, then there have been some changes and call the callback. Can be nicely wrapped in a promise. You can even call your custom event instead of calling callback.

The method is good, fast, if the div not many kilometers long. The most important thing is that it is very, very cross-browser, it will work even in ie7. But if the div is large, then it can cause brakes.

Alternative 2, what you need


The second way is correct. After a failed attempt with special events, they came up with, it really works in good browsers, MutationObserver () , and if not, then its polyfill . How do I use it? But like this.

First, let's create a MutationObserver object:

var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        console.dir(mutation); //объект с изменениями
    });    
});

In this case, mutations is an array MutationRecord , each element of which contains the following fields:

  1. type – all changed attributes or characterData (text element or comment) or childList depending on the type of mutation;
  2. target – the element of the tree in which the change occurred;
  3. addedNodesNodeList containing the added tree elements, it is logical that it will be empty ( *.length == 0 ) if there are no such elements.
  4. removedNodesNodeList containing removed tree elements, it will also be empty if there are no such items;
  5. previousSibling previous tree sibling of the added or removed element, or null ;
  6. nextSibling is the next tree nextSibling added or removed element, or null ;
  7. attributeName – the name of the changed attribute or null ;
  8. oldValue – the old values, before the change, but in case the change happened in the childListnull .

Then, for this charm to work, let's say to the interpreter "start tracking":

observer.observe(
    elem,
    {
        childList: true,
        attributes: true,
        subtree: true,
        characterData: true,
        attributeOldValue: true,
        characterDataOldValue: true,
        attributeFilter: true
    }
);

As the first argument, we pass the DOM object to monitor, the second is how we will monitor:

  1. childList – monitor the insertion / removal of elements in the childList ;
  2. attributes – watch for changes in attributes
  3. characterData – watch for changes in characterData (text items or comments)
  4. attributeOldValue – remember the value of the attribute before changing
  5. characterDataOldValue – remember the value of characterData before changing
  6. attributeFilterArray the attributes to monitor

To stop tracking, you can call the observer.disconnect(); method observer.disconnect();

Warning


I do not advise changing the DOM element that we are monitoring, it can be a very tricky situation.

Forgotten for later last alternative


It is worth mentioning that there is a standard event that is completely cross-browser and works – onchange .

It works on <input> , <select> , and <textarea> elements. Fires when, alas, only the user changes the data inside only these objects. When changing the data in the script, the event must be called with your own hand.

Scroll to Top