亚洲免费不卡_在线视频精品_国产尤物精品_久久久久网址_久久精品91_欧美va天堂在线_狠狠入ady亚洲精品_亚洲午夜精品福利_国产精品草草_午夜精品久久99蜜桃的功能介绍

基于HTML5新特性Mutation Observer實現編輯器的撤銷和回退操作
來源:易賢網 閱讀:1834 次 日期:2016-07-09 10:01:45
溫馨提示:易賢網小編為您整理了“基于HTML5新特性Mutation Observer實現編輯器的撤銷和回退操作”,方便廣大網友查閱!

Mutation Observer(變動觀察器)是監視DOM變動的接口。當DOM對象樹發生任何變動時,Mutation Observer會得到通知,本文給大家分享基于HTML5新特性Mutation Observer實現編輯器的撤銷和回退操作,感興趣的朋友參考下

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>

更多信息請查看網頁制作
由于各方面情況的不斷調整與變化,易賢網提供的所有考試信息和咨詢回復僅供參考,敬請考生以權威部門公布的正式信息和咨詢為準!
關于我們 | 聯系我們 | 人才招聘 | 網站聲明 | 網站幫助 | 非正式的簡要咨詢 | 簡要咨詢須知 | 新媒體/短視頻平臺 | 手機站點

版權所有:易賢網

亚洲免费不卡_在线视频精品_国产尤物精品_久久久久网址_久久精品91_欧美va天堂在线_狠狠入ady亚洲精品_亚洲午夜精品福利_国产精品草草_午夜精品久久99蜜桃的功能介绍
国产一区二区在线观看免费播放| 久久不射中文字幕| 欧美资源在线| 中文精品在线| 亚洲国产日本| 国内精品久久国产| 欧美欧美天天天天操| 麻豆精品91| 久久久久欧美| 欧美激情五月| 欧美日韩精品免费观看| 欧美日韩一区二区三区在线视频 | 午夜日韩激情| 美女91精品| 欧美一级视频| 欧美91大片| 欧美区国产区| 国产在线精品二区| 一区精品在线| 一本色道久久精品| 亚洲一区二区免费看| 免费不卡亚洲欧美| 欧美区一区二| 日韩香蕉视频| 免费久久久一本精品久久区| 欧美一区免费视频| 国产综合色产| 国产亚洲一区在线| 99精品国产高清一区二区| 在线视频欧美一区| 久久大逼视频| 欧美精品午夜| 在线日韩av| 国产精品日韩欧美一区二区| 亚洲激情一区二区| 欧美中文字幕| 国模精品娜娜一二三区| 99精品国产福利在线观看免费| 一区在线免费| 久久精品91| 亚洲国产精品久久久久久女王 | 国产偷国产偷亚洲高清97cao| 中文亚洲免费| 欧美freesex交免费视频| 伊人久久婷婷| 老司机一区二区三区| 欧美成熟视频| 亚洲亚洲精品三区日韩精品在线视频| 在线综合亚洲| 亚洲一二区在线| 麻豆av一区二区三区久久| 激情文学一区| 欧美片第1页综合| 欧美日韩国产成人精品| 欧美日韩在线播放一区二区| 乱码第一页成人| 久久久天天操| 欧美激情亚洲| 欧美视频一区| 在线免费观看欧美| 国产日韩一区二区三区| 中文亚洲字幕| 香蕉国产精品偷在线观看不卡| 蘑菇福利视频一区播放| 欧美精品一区二区三区在线看午夜| 亚欧成人精品| 欧美女人交a| 91久久综合| 国产精品一区二区a| 亚洲一区亚洲| 欧美片第1页综合| 亚洲国产影院| 久久婷婷人人澡人人喊人人爽| 欧美精品二区三区四区免费看视频| 黄页网站一区| 欧美亚洲视频| 在线 亚洲欧美在线综合一区| 亚洲精品美女91| 可以看av的网站久久看| 91久久久一线二线三线品牌| 久久xxxx精品视频| 亚洲成人自拍视频| 久久人人超碰| 99在线热播精品免费99热| 久久久www| 亚洲精品四区| 欧美色123| 麻豆精品网站| 亚洲少妇自拍| 亚洲香蕉网站| 久久综合影音| 亚洲综合社区| 日韩午夜黄色| 黑人一区二区三区四区五区| 久久国产精品久久精品国产| 亚洲精品一区二区三| 欧美fxxxxxx另类| 亚洲免费在线精品一区| 亚洲精选成人| 欧美日韩一区二区三区在线观看免| 国产精品亚洲欧美| 日韩午夜av| 亚洲福利免费| 伊人影院久久| 影音先锋日韩资源| 国产精品激情| 欧美精品一区二区视频| 亚洲制服av| 国产精品视区| 国产一区二区高清视频| 99精品99久久久久久宅男| 亚洲国产高清视频| 在线成人国产| 在线观看欧美亚洲| 影音先锋亚洲一区| 亚洲国产精品第一区二区| 激情偷拍久久| 国产在线观看一区| 好吊视频一区二区三区四区| 国产精品hd| 黄色亚洲在线| 在线视频观看日韩| 一本色道久久综合亚洲二区三区| 亚洲裸体俱乐部裸体舞表演av| 99国产精品私拍| 国产一区二区三区久久| 噜噜噜噜噜久久久久久91| 裸体丰满少妇做受久久99精品| 麻豆av一区二区三区| 欧美精品观看| 亚洲无吗在线| 亚洲精品欧美| 亚洲免费在线精品一区| 欧美一区在线看| 国内自拍一区| 国产欧美日韩一区二区三区在线| 国产三级精品在线不卡| 久久亚洲不卡| 在线免费观看欧美| 欧美亚洲一区| 国产一区亚洲| 国产精品日本| 欧美日产一区二区三区在线观看| 红桃视频亚洲| 亚洲一区二区三区四区中文| 欧美精品aa| 亚洲久久一区二区| 久久亚洲综合| 亚洲欧洲一区二区在线观看| 国产精品视频久久一区| 午夜亚洲福利| 国产精品一区在线观看| 欧美日韩大片一区二区三区| 亚洲精品在线观看免费| 久久久噜噜噜久久狠狠50岁| 亚洲午夜激情| 你懂的一区二区| 亚洲国产一区二区三区在线播| 亚洲永久视频| 在线观看成人av电影| 蜜桃精品久久久久久久免费影院| 在线视频观看日韩| 久久一区激情| 亚洲欧美日韩国产一区二区| 1024精品一区二区三区| 女人色偷偷aa久久天堂| 国产日韩欧美二区| 亚洲一本视频| 欧美精品日韩| 久久天天狠狠| 国产精品推荐精品| 亚洲精品美女久久7777777| 欧美日韩ab| 老牛嫩草一区二区三区日本 | 美女日韩在线中文字幕| 亚洲三级国产| 国产精品v亚洲精品v日韩精品| 香蕉av777xxx色综合一区| 99精品福利视频| 国产在线日韩| 欧美在线不卡| 久久久久久九九九九| 午夜在线a亚洲v天堂网2018| av不卡免费看| 亚洲日韩成人| 在线日本成人| 亚洲精品社区| 一区二区精品在线| 99精品国产高清一区二区 | 日韩午夜在线| 亚洲网址在线| 亚洲高清免费| 136国产福利精品导航网址| 国模精品一区二区三区| 激情久久一区| 日韩亚洲精品在线| 亚洲精品国产日韩| 国产欧美日韩视频一区二区三区| 一本色道久久综合亚洲二区三区| 亚洲精品国产系列| 一本色道久久精品| 国产精品日韩欧美一区二区三区| 中文欧美日韩| 亚洲一区二区三区精品视频| 亚洲欧美视频| 久久综合影音| 亚洲视频观看| 中文一区二区| 久久狠狠一本精品综合网| 久久av一区二区| 欧美激情成人在线| 亚洲国产mv| 国产精品区一区| 久久综合狠狠| 永久域名在线精品| 亚洲看片一区| 久久午夜精品一区二区| 国产精品yjizz| 一区二区三区国产盗摄| 老鸭窝91久久精品色噜噜导演| 欧美不卡视频| 亚洲精品一二| 久热精品视频| 亚洲美女啪啪| 久久天堂成人| 91久久在线| 老司机午夜精品视频| 在线日韩av| 欧美一区二区三区免费看| 亚洲第一精品影视| 噜噜噜久久亚洲精品国产品小说| 好看的亚洲午夜视频在线| 国产一区二区黄色| 国产一在线精品一区在线观看| 99精品视频免费| 欧美一区三区二区在线观看| 亚洲作爱视频| 国产精品av久久久久久麻豆网| av成人激情| 国产精品hd| 免费在线一区二区| 99香蕉国产精品偷在线观看| 欧美日韩18| 可以免费看不卡的av网站| 国产区二精品视| 狠狠色狠狠色综合人人| 欧美一区二区三区另类| 一本色道久久综合亚洲精品婷婷| 欧美午夜视频在线| 久久男女视频| 香蕉久久夜色精品| 日韩一级大片| 亚洲小说区图片区| 欧美欧美天天天天操| 久久亚洲欧美| 男女av一区三区二区色多| 在线视频精品一区| 亚洲午夜激情| 好看的亚洲午夜视频在线| 欧美日韩精品免费观看视一区二区| 亚洲影院在线| 亚洲在线观看| 亚洲一卡久久| 国产亚洲福利| 国产精品久久波多野结衣| 国产精品入口| 国内在线观看一区二区三区| 亚洲毛片播放| 欧美精品一区二区视频 | 一区二区三区我不卡| 欧美精品色网| 久久这里只有| 久久亚洲免费| 麻豆av一区二区三区久久| 久久人人97超碰人人澡爱香蕉| 一本综合精品| 99亚洲一区二区| 久久久777| 欧美精品一区二区三区久久久竹菊| 精品福利av| 一区在线电影| 99riav1国产精品视频| 99视频在线精品国自产拍免费观看| 亚洲精品一级| 亚洲一区自拍| 欧美一区二区三区免费看| 国产精品mm| 亚洲精品极品| 噜噜噜在线观看免费视频日韩| 久久资源在线| 亚洲大片在线| 亚洲欧美日韩另类精品一区二区三区| 亚洲一区在线免费| 久久xxxx| 国内不卡一区二区三区| 99国产精品久久久久久久| 国产精品视频福利| 午夜久久黄色| 亚洲伦理一区| 女人香蕉久久**毛片精品| 激情久久婷婷| 亚洲在线播放| 伊人久久婷婷| 久久久福利视频| 亚洲黄页一区| 久久精品亚洲一区二区| 在线观看视频免费一区二区三区| 国产精品一区二区欧美| 国产在线精品二区| 亚洲一区欧美激情| 在线精品亚洲一区二区| 六月丁香综合| 欧美日韩国产成人精品| 一本久久知道综合久久| 欧美在线播放一区二区| 亚洲精品裸体| 欧美成人综合| 亚洲一区激情| 亚洲青涩在线| 国产一区二区三区四区hd| 亚洲一区二区三区免费在线观看| 欧美日韩视频| 久久精品二区三区| 夜夜夜久久久| 亚洲国产成人不卡| 欧美激情视频一区二区三区在线播放 | 久久午夜影视| 亚洲欧洲精品一区| 国产精品初高中精品久久 | 亚洲高清激情| 欧美视频一区| 欧美日本中文| 欧美日本不卡高清| 久久在线精品| 久久久夜夜夜| 久久免费一区| 欧美主播一区二区三区美女 久久精品人| 在线看片一区| 亚洲性视频h| 一区久久精品| 亚洲性人人天天夜夜摸| 欧美午夜不卡影院在线观看完整版免费| 噜噜噜91成人网| 女主播福利一区| 欧美日韩精品免费观看| 欧美三级特黄| 国产在线欧美| 亚洲第一网站| 一本色道婷婷久久欧美| 中文亚洲欧美| 久久精品国语| 欧美日韩在线播放一区二区| 黄色精品网站| 99人久久精品视频最新地址| 亚洲每日在线| 欧美亚洲免费| 欧美日本不卡高清| 亚洲福利免费| 一区二区三区导航| 美女国产一区| 狠狠色狠狠色综合人人| 亚洲精品无人区| 免费欧美在线| 欧美三区在线| 一区二区三区四区五区精品| 母乳一区在线观看| 欧美日韩亚洲国产精品| 亚洲人成人一区二区三区| 亚洲欧美日本视频在线观看| 欧美激情一区| 99亚洲视频| 欧美fxxxxxx另类| 亚洲经典在线| 国产精品porn| 午夜在线精品| 欧美在线视频一区二区三区| 欧美日韩亚洲国产精品| 日韩午夜av| 欧美成人一区二免费视频软件| 欧美日韩大片一区二区三区| 亚洲精选成人| 欧美88av| 国产亚洲福利| 国户精品久久久久久久久久久不卡| av不卡在线| 欧美日韩一卡| 国产嫩草一区二区三区在线观看| 欧美成熟视频| 亚洲综合不卡| 亚洲一级特黄| 久久一区二区三区av| 亚洲激情黄色| 欧美激情1区| 国产精品五区| 亚洲青色在线| 好吊一区二区三区| 久久亚洲精品欧美| 国产精品久久久久久久久婷婷| 国产精品激情电影| 久久人人97超碰国产公开结果|