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:
-
DOMAttrModified
– if the attribute of the DOM element has changed; -
DOMAttributeNameChanged
– if the name of the attribute on the DOM element has changed; -
DOMCharacterDataModified
– if any text has changed, be it a regular 'text node' or a simple comment on a DOM element; -
DOMElementNameChanged
– if the name of the DOM element has changed; -
DOMNodeInserted
– if the DOM element has a new element inserted; -
DOMNodeInsertedIntoDocument
– if a new element was inserted into thedocument
; -
DOMNodeRemoved
– if any element has been removed from the DOM element; -
DOMNodeRemovedFromDocument
– if any element indocument
been deleted; -
DOMSubtreeModified
– if there has been any change todocument
.
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:
- come up with a name for the event, for example,
DOM:changed
; - create handler functions and subscribe them to these events;
- find all functions that can potentially change the DOM;
- 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:
- take the reference
.innerHTML
from yourdiv
and save it anywhere, for example, insessionStorage
- 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:
-
type
– all changed attributes orcharacterData
(text element or comment) orchildList
depending on the type of mutation; -
target
– the element of the tree in which the change occurred; -
addedNodes
–NodeList
containing the added tree elements, it is logical that it will be empty (*.length == 0
) if there are no such elements. -
removedNodes
–NodeList
containing removed tree elements, it will also be empty if there are no such items; -
previousSibling
previous tree sibling of the added or removed element, ornull
; -
nextSibling
is the next treenextSibling
added or removed element, ornull
; -
attributeName
– the name of the changed attribute ornull
; -
oldValue
– the old values, before the change, but in case the change happened in thechildList
–null
.
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:
-
childList
– monitor the insertion / removal of elements in thechildList
; -
attributes
– watch for changes in attributes -
characterData
– watch for changes incharacterData
(text items or comments) -
attributeOldValue
– remember the value of the attribute before changing -
characterDataOldValue
– remember the value ofcharacterData
before changing -
attributeFilter
–Array
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.