Introduction to MutationObserver
MutationObserver provides developers with the ability to respond appropriately when the DOM tree within a certain range changes. This API is designed to replace the Mutation event introduced in the DOM3 event specification.
Mutation Observer is an interface for monitoring DOM changes. Mutation Observer will be notified when any changes occur in the DOM object tree.
Mutation Observer has the following features:
•It waits for all script tasks to be completed before running, that is, in an asynchronous manner
•It encapsulates DOM change records into an array for processing instead of processing DOM changes one by one.
•It can observe all changes that occur in DOM nodes, and it can also observe a certain type of changes
MDN information: MutationObserver
MutationObserver is a constructor, so when creating it, you need to pass new MutationObserver;
When instantiating MutationObserver, a callback function is required. The callback function will be called when the specified DOM node (target node) changes.
When called, the observer object will be passed to the function with two parameters:
1: The first parameter is an array containing several MutationRecord objects;
2: The second parameter is the observer object itself.
For example:
var observer = new MutationObserver(function(mutations ) {
mutations.forEach(function(mutation) {
console.log(mutation.type);
});
});
observer methods
The instance observer has three methods: 1: observe ; 2: disconnect ; 3: takeRecords ;
observe method
Observe method: Register the target node that needs to be observed to the current observer object, and receive a notification when the DOM changes in the target node (you can also observe its descendant nodes at the same time);
This method requires two parameters, the first is the target node, and the second parameter is the type that needs to be monitored for changes, which is a json object. The example is as follows:
observer.observe( document.body, {
'childList': true, //The child elements of this element are added or deleted
'subtree': true, //All the child elements of this element are added or deleted
'attributes' : true, //Listen for attribute changes
'characterData' : true, //Listen for text or comment changes
'attributeOldValue' : true, //Original value of attribute
'characterDataOldValue' : true
});
disconnect method
The disconnect method will stop observing the properties and node changes of the target node until the observe method is called again next time;
takeRecords
Clear the record queue of the observer object and return an array containing the Mutation event object;
MutationObserver is perfect for implementing redo and undo of an editor, because any changes that occur within the specified node will be recorded every time. If you use traditional keydown or keyup implementation, there will be some disadvantages, such as:
1: Loss of scrolling, resulting in inaccurate scroll position;
2: Lost focus;
....
It took a few hours to write a management plug-in MutationJS for undo and redo (management of undo and rollback) implemented through MutationObserver, which can be used as a Separate plug-in introduction: (http://files.cnblogs.com/files/diligenceday/MutationJS.js):
/**
* @desc MutationJs, uses DOM3's new event MutationObserve; by listening to specified node elements, listens for changes in internal dom attributes or dom nodes, and executes corresponding callbacks;
**/
window.nono = window.nono || {};
/**
* @desc
**/
nono.MutationJs = function( dom ) {
//Uniform compatibility issues
var MutationObserver = this.MutationObserver = window.MutationObserver ||
window.WebKitMutationObserver ||
window.MozMutationObserver;
//Determine whether the browser is or Whether to support MutationObserver;
this.mutationObserverSupport = !!MutationObserver;
//By default, listen for changes in child elements, attributes of child elements, and attribute values;
this.options = {
'childList': true,
'subtree' : true,
'attributes' : true,
'characterData' : true,
'attributeOldValue' : true,
'characterDataOldValue' : true
} ;
//This saves the instance of MutationObserve;
this.muta = {};
//The list variable saves the user's operations;
this.list = [];
//The current rollback index
this.index = 0;
//If there is no dom, it will listen to body by default;
this.dom = dom|| document.documentElement.body || document. getElementsByTagName("body")[0];
//Start monitoring immediately;
this.observe( );
};
$.extend(nono.MutationJs.prototype, {
//Callback for node changes, you need to save redo and undo to the list;
"callback" : function ( records , instance ) {
//You need to clear the ones behind the index;
this .list.splice( this.index 1 );
var _this = this;
records.map(function(record) {
var target = record.target;
console.log(record) ;
//Delete or add elements;
if( record.type === "childList" ) {
//If you delete elements;
if(record.removedNodes.length ! == 0) {
//Get the relative index of the element;
var indexs = _this.getIndexs(target.children, record.removedNodes);
_this.list.push({
"undo " : function() {
_this.disconnect();
_this.addChildren(target, record.removedNodes ,indexs );
_this.reObserve();
},
"redo " : function() {
_this.disconnect();
_this.removeChildren(target, record.removedNodes );
_this.reObserve();
}
});
//If elements are added;
};
if(record.addedNodes.length !== 0) {
//Get the relative index of the element;
var indexs = _this.getIndexs( target.children , record.addedNodes );
_this.list.push({
"undo" : function() {
_this.disconnect();
_this.removeChildren(target, record. addedNodes );
_this.reObserve();
},
"redo" : function () {
_this.disconnect();
_this.addChildren(target, record.addedNodes, indexes);
_this.reObserve();
}
});
};
//@desc What the hell is characterData;
//ref: http:// baike.baidu.com/link?url=Z3Xr2y7zIF50bjXDFpSlQ0PiaUPVZhQJO7SaMCJXWHxD6loRcf_TVx1vsG74WUSZ_0-7wq4_oq0Ci-8ghUAG8a
}else if( record.type === "characterData" ) {
var oldValue = record. oldValue;
var newValue = record .target.textContent //|| record.target.innerText, is not prepared to handle IE789 compatibility, so innerText is not needed;
_this.list.push({
"undo" : function() {
_this.disconnect();
target.textContent = oldValue;
_this.reObserve();
},
"redo" : function () {
_this.disconnect();
target.textContent = newValue;
_this.reObserve();
}
});
//If the attributes change, style, dataset, attribute all belong to attributes changes, you can Unified processing;
}else if( record.type === "attributes" ) {
var oldValue = Record.oldValue;
var newValue = Record.target.getAttribute( Record.attributeName );
var attributeName = Record.attributeName;
_this.list.push({
"실행 취소" : function() {
_this.disconnect();
target.setAttribute(attributeName, oldValue);
_this.reObserve();
},
"redo" : function() {
_this.disconnect();
target.setAttribute(attributeName, newValue);
_this.reObserve();
}
});
};
} );
//중신설设置索引;
this.index = this.list.length-1;
},
"removeChildren" : 함수( 대상, 노드 ) {
for( var i= 0, len= node.length; i target.removeChild( node[i] );
};
},
"addChildren": 함수( 대상, 노드, 인덱스) {
for(var i= 0, len= 노드.길이; 나는 if(target.children[ indexs[i] ]) {
target.insertBefore( 노드[i] , target.children[ 인덱스[i] ]) ;
}else{
target.appendChild( 노드[i] );
};
};
},
//快捷방법,사용来判断child재父元素的哪个节点上;
" indexOf": 함수(target, obj) {
return Array.prototype.indexOf.call(target, obj)
},
"getIndexs": 함수(target, objs) {
var 결과 = [];
for(var i=0; i result.push( this.indexOf(target, objs[i]) );
};
return result;
},
/**
* @desc는 청취 객체를 지정합니다
**/
"observe" : function( ) {
if( this.dom.nodeType !== 1) return Alert("参数不对,第一个参数应该为一个dom节点");
this.muta = new this.MutationObserver( this.callback.bind(this) );
//马上开始监听;
this .muta.observe( this.dom, this.options );
},
/**
* @desc 재시작 모니터링;
**/
"reObserve" : 함수 () {
this.muta.observe( this.dom, this.options );
},
/**
*@desc는 DOM 작업을 기록하지 않으며 이 함수 내의 모든 작업은 실행 취소 및 다시 실행 목록에 기록되지 않습니다.
**/
"without" : function ( fn ) {
this.disconnect();
fn&fn ();
this.reObserve();
},
/**
* @desc 모니터링 취소;
**/
"disconnect" : function () {
return this.muta.disconnect() ;
},
/**
* @desc Mutation 작업을 목록에 저장합니다.
**/
"save" : function ( obj ) {
if(!obj.undo)return warning("传进来的第一个参数必须 유 실행 취소 방법 실행");
if(!obj.redo)return 경고("传进来的第一个参数必须유재실행 방법 실행");
this.list.push(obj) ;
},
/**
* @desc ;
**/
"reset" : function () {
//清空数组;
this.list = [];
this .index = 0;
},
/**
* @desc 지정된 인덱스 뒤의 작업을 삭제합니다.
**/
"splice" : 함수( 인덱스 ) {
this.list.splice( 인덱스 );
},
/**
* @desc 뒤로 이동, 롤백 취소
**/
"undo" : function () {
if( this.canUndo() ) {
this.list[this.index].undo();
this.index--;
};
},
/**
* @desc 계속해서 다시 하세요
**/
"redo" : function () {
if( this.canRedo( ) ) {
this.index ;
this.list[this.index].redo();
};
},
/**
* @desc는 작업을 취소할 수 있는지 여부를 결정합니다
**/
"canUndo": 함수() {
return this.index !== -1;
},
/**
* @desc는 재작동 가능 여부를 결정합니다.
**/
"canRedo": 함수() {
return this.list.length-1 !== this.index;
}
});
MutationJS如何使사용
那么这个MutationJS如何使사용呢?
//This is to instantiate a MutationJS object. If no parameters are passed, it will monitor changes in the body element by default;
mu = new nono.MutationJs();
//You can pass a specified element. For example:
mu = new nono.MutationJS( document.getElementById("div0") );
//Then all element changes under this element will be recorded by the plug-in;
Mutation instance mu has several methods:
1: mu.undo() operation rollback;
2: mu.redo() Undo rollback;
3: Whether mu.canUndo() can operate back, the return value is true or false;
4: Whether mu.canRedo() can cancel the rollback, the return value is true or false;
5: mu.reset() clears all undo lists and releases space;
6: mu.without() passes a function parameter, and all DOM operations inside the function will not be recorded by mu;
MutationJS implements a simple undoManager for reference, which runs perfectly on Firefox, Chrome, Google Chrome, and IE11:
<script><br> window.onload = function () {<br> window.mu = new nono.MutationJs();<br> //Cancel monitoring<br> mu.disconnect();<br> //Restart monitoring<br> mu.reObserve(); <br> document.getElementById("b0").addEventListener("click", function ( ev ) {<br> div = document.createElement("div");<br> div.innerHTML = document.getElementById("value ").value;<br> document.getElementById("div").appendChild( div );<br> });<br> document.getElementById("prev").addEventListener("click", function ( ev ) {<br> mu.undo();<br> });<br> document.getElementById("next").addEventListener("click", function ( ev ) {<br> mu.redo();<br> });<br> };<br></script>
Screenshot of DEMO under IE:
Browser compatibility of MutatoinObserver:
Feature |
Chrome |
Firefox (Gecko) |
Internet Explorer |
Opera |
Safari |
Basic support |
|
14(14) |
11 |
15 |
6.0WebKit
|