好湿?好紧?好多水好爽自慰,久久久噜久噜久久综合,成人做爰A片免费看黄冈,机机对机机30分钟无遮挡

主頁 > 知識庫 > 基于HTML5新特性Mutation Observer實現編輯器的撤銷和回退操作

基于HTML5新特性Mutation Observer實現編輯器的撤銷和回退操作

熱門標簽:呼和浩特外呼電銷系統排名 南通數據外呼系統推廣 外呼系統電話怎么投訴 阜陽企業外呼系統 外呼線穩定線路 地圖標注位置能賺錢嗎 申請400電話流程簡介 pageadm實現地圖標注 邢臺縣地圖標注app

MutationObserver介紹

MutationObserver給開發者們提供了一種能在某個范圍內的DOM樹發生變化時作出適當反應的能力.該API設計用來替換掉在DOM3事件規范中引入的Mutation事件.

Mutation Observer(變動觀察器)是監視DOM變動的接口。當DOM對象樹發生任何變動時,Mutation Observer會得到通知。

Mutation Observer有以下特點:

 •它等待所有腳本任務完成后,才會運行,即采用異步方式
 •它把DOM變動記錄封裝成一個數組進行處理,而不是一條條地個別處理DOM變動。
 •它即可以觀察發生在DOM節點的所有變動,也可以觀察某一類變動

MDN的資料: MutationObserver

MutationObserver是一個構造函數, 所以創建的時候要通過 new MutationObserver;

實例化MutationObserver的時候需要一個回調函數,該回調函數會在指定的DOM節點(目標節點)發生變化時被調用,

在調用時,觀察者對象會 傳給該函數 兩個參數:

    1:第一個參數是個包含了若干個MutationRecord對象的數組;

    2:第二個參數則是這個觀察者對象本身.

比如這樣:

     

復制代碼
代碼如下:

var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(mutation.type);
});
});

observer的方法

實例observer有三個方法: 1: observe  ;2: disconnect ; 3: takeRecords   ;

observe方法

observe方法:給當前觀察者對象注冊需要觀察的目標節點,在目標節點(還可以同時觀察其后代節點)發生DOM變化時收到通知;

這個方法需要兩個參數,第一個為目標節點, 第二個參數為需要監聽變化的類型,是一個json對象,  實例如下:

       

復制代碼
代碼如下:

observer.observe( document.body, {
'childList': true, //該元素的子元素新增或者刪除
'subtree': true, //該元素的所有子元素新增或者刪除
'attributes' : true, //監聽屬性變化
'characterData' : true, // 監聽text或者comment變化
'attributeOldValue' : true, //屬性原始值
'characterDataOldValue' : true
});

disconnect方法

disconnect方法會停止觀察目標節點的屬性和節點變化, 直到下次重新調用observe方法;

takeRecords

清空 觀察者對象的 記錄隊列,并返回一個數組, 數組中包含Mutation事件對象;

MutationObserver實現一個編輯器的redo和undo再適合不過了, 因為每次指定節點內部發生的任何改變都會被記錄下來, 如果使用傳統的keydown或者keyup實現會有一些弊端,比如:

1:失去滾動, 導致滾動位置不準確;

2:失去焦點;
....
用了幾小時的時間,寫了一個通過MutationObserver實現的 undo 和 redo (撤銷回退的管理)的管理插件 MutationJS ,   可以作為一個單獨的插件引入:(http://files.cnblogs.com/files/diligenceday/MutationJS.js):


復制代碼
代碼如下:

/**
* @desc MutationJs, 使用了DOM3的新事件 MutationObserve; 通過監聽指定節點元素, 監聽內部dom屬性或者dom節點的更改, 并執行相應的回調;
* */
window.nono = window.nono || {};
/**
* @desc
* */
nono.MutationJs = function( dom ) {
//統一兼容問題
var MutationObserver = this.MutationObserver = window.MutationObserver ||
window.WebKitMutationObserver ||
window.MozMutationObserver;
//判斷瀏覽器是或否支持MutationObserver;
this.mutationObserverSupport = !!MutationObserver;
//默認監聽子元素, 子元素的屬性, 屬性值的改變;
this.options = {
'childList': true,
'subtree': true,
'attributes' : true,
'characterData' : true,
'attributeOldValue' : true,
'characterDataOldValue' : true
};
//這個保存了MutationObserve的實例;
this.muta = {};
//list這個變量保存了用戶的操作;
this.list = [];
//當前回退的索引
this.index = 0;
//如果沒有dom的話,就默認監聽body;
this.dom = dom|| document.documentElement.body || document.getElementsByTagName("body")[0];
//馬上開始監聽;
this.observe( );
};
$.extend(nono.MutationJs.prototype, {
//節點發生改變的回調, 要把redo和undo都保存到list中;
"callback" : function ( records , instance ) {
//要把索引后面的給清空;
this.list.splice( this.index+1 );
var _this = this;
records.map(function(record) {
var target = record.target;
console.log(record);
//刪除元素或者是添加元素;
if( record.type === "childList" ) {
//如果是刪除元素;
if(record.removedNodes.length !== 0) {
//獲取元素的相對索引;
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(record.addedNodes.length !== 0) {
//獲取元素的相對索引;
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 ,indexs);
_this.reObserve();
}
});
};
//@desc 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, 不準備處理IE789的兼容,所以不用innerText了;
_this.list.push({
"undo" : function() {
_this.disconnect();
target.textContent = oldValue;
_this.reObserve();
},
"redo" : function () {
_this.disconnect();
target.textContent = newValue;
_this.reObserve();
}
});
//如果是屬性變化的話style, dataset, attribute都是屬于attributes發生改變, 可以統一處理;
}else if( record.type === "attributes" ) {
var oldValue = record.oldValue;
var newValue = record.target.getAttribute( record.attributeName );
var attributeName = record.attributeName;
_this.list.push({
"undo" : 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" : function ( target, nodes ) {
for(var i= 0, len= nodes.length; i<len; i++ ) {
target.removeChild( nodes[i] );
};
},
"addChildren" : function ( target, nodes ,indexs) {
for(var i= 0, len= nodes.length; i<len; i++ ) {
if(target.children[ indexs[i] ]) {
target.insertBefore( nodes[i] , target.children[ indexs[i] ]) ;
}else{
target.appendChild( nodes[i] );
};
};
},
//快捷方法,用來判斷child在父元素的哪個節點上;
"indexOf" : function ( target, obj ) {
return Array.prototype.indexOf.call(target, obj)
},
"getIndexs" : function (target, objs) {
var result = [];
for(var i=0; i<objs.length; 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" : function () {
this.muta.observe( this.dom, this.options );
},
/**
*@desc 不記錄dom操作, 所有在這個函數內部的操作不會記錄到undo和redo的列表中;
* */
"without" : function ( fn ) {
this.disconnect();
fn&fn();
this.reObserve();
},
/**
* @desc 取消監聽;
* */
"disconnect" : function () {
return this.muta.disconnect();
},
/**
* @desc 保存Mutation操作到list;
* */
"save" : function ( obj ) {
if(!obj.undo)return alert("傳進來的第一個參數必須有undo方法才行");
if(!obj.redo)return alert("傳進來的第一個參數必須有redo方法才行");
this.list.push(obj);
},
/**
* @desc ;
* */
"reset" : function () {
//清空數組;
this.list = [];
this.index = 0;
},
/**
* @desc 把指定index后面的操作刪除;
* */
"splice" : function ( index ) {
this.list.splice( index );
},
/**
* @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" : function () {
return this.index !== -1;
},
/**
* @desc 判斷是否可以重新操作;
* */
"canRedo" : function () {
return this.list.length-1 !== this.index;
}
});

MutationJS如何使用

那么這個MutationJS如何使用呢?


復制代碼
代碼如下:

//這個是實例化一個MutationJS對象, 如果不傳參數默認監聽body元素的變動;
mu = new nono.MutationJs();
//可以傳一個指定元素,比如這樣;
mu = new nono.MutationJS( document.getElementById("div0") );
//那么所有該元素下的元素變動都會被插件記錄下來;

Mutation的實例mu有幾個方法:

1:mu.undo()  操作回退;

2:mu.redo()   撤銷回退;

3:mu.canUndo() 是否可以操作回退, 返回值為true或者false;

4:mu.canRedo() 是否可以撤銷回退, 返回值為true或者false;

5:mu.reset() 清空所有的undo列表, 釋放空間;

6:mu.without() 傳一個為函數的參數, 所有在該函數內部的dom操作, mu不做記錄;

MutationJS實現了一個簡易的 undoManager 提供參考,在火狐和chrome,谷歌瀏覽器,IE11上面運行完全正常:


復制代碼
代碼如下:

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<script src="http://cdn.bootcss.com/jquery/1.9.0/jquery.js"></script>
<script src="http://files.cnblogs.com/files/diligenceday/MutationJS.js"></script>
</head>
<body>
<div>
<p>
MutationObserver是為了替換掉原來Mutation Events的一系列事件, 瀏覽器會監聽指定Element下所有元素的新增,刪除,替換等;
</p>
<div style="padding:20px;border:1px solid #f00">
<input type="button" value="撤銷操作" id="prev">;
<input type="button" value="撤銷操作回退" id="next">;
</div>
<input type="button" value="添加節點" id="b0">;
<input value="text" id="value">
<div id="div"></div>
</div>
<script>
window.onload = function () {
window.mu = new nono.MutationJs();
//取消監聽
mu.disconnect();
//重新監聽
mu.reObserve();
document.getElementById("b0").addEventListener("click", function ( ev ) {
div = document.createElement("div");
div.innerHTML = document.getElementById("value").value;
document.getElementById("div").appendChild( div );
});
document.getElementById("prev").addEventListener("click", function ( ev ) {
mu.undo();
});
document.getElementById("next").addEventListener("click", function ( ev ) {
mu.redo();
});
};
</script>
</body>
</html>

DEMO在IE下的截圖:

MutatoinObserver的瀏覽器兼容性:

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari
Basic support
18

webkit

26

14(14) 11 15 6.0WebKit

標簽:辛集 內蒙古 鶴崗 黃山 楊凌 德州 撫順 蚌埠

巨人網絡通訊聲明:本文標題《基于HTML5新特性Mutation Observer實現編輯器的撤銷和回退操作》,本文關鍵詞  基于,HTML5,新特性,新,特性,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《基于HTML5新特性Mutation Observer實現編輯器的撤銷和回退操作》相關的同類信息!
  • 本頁收集關于基于HTML5新特性Mutation Observer實現編輯器的撤銷和回退操作的相關信息資訊供網民參考!
  • 推薦文章