真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

如何使用HTML5新特性MutationObserver實現(xiàn)編輯器的撤銷和回退操作

這篇文章主要介紹如何使用HTML5新特性Mutation Observer實現(xiàn)編輯器的撤銷和回退操作,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們一定要看完!

公司主營業(yè)務(wù):做網(wǎng)站、成都網(wǎng)站制作、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)公司是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊有機(jī)會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)公司推出北關(guān)免費做網(wǎng)站回饋大家。

MutationObserver介紹

MutationObserver給開發(fā)者們提供了一種能在某個范圍內(nèi)的DOM樹發(fā)生變化時作出適當(dāng)反應(yīng)的能力.該API設(shè)計用來替換掉在DOM3事件規(guī)范中引入的Mutation事件.

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

Mutation Observer有以下特點:

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

MDN的資料: MutationObserver

MutationObserver是一個構(gòu)造函數(shù), 所以創(chuàng)建的時候要通過 new MutationObserver;

實例化MutationObserver的時候需要一個回調(diào)函數(shù),該回調(diào)函數(shù)會在指定的DOM節(jié)點(目標(biāo)節(jié)點)發(fā)生變化時被調(diào)用,

在調(diào)用時,觀察者對象會 傳給該函數(shù) 兩個參數(shù):

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

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

比如這樣:

     

代碼如下:


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

observer的方法

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

observe方法

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

這個方法需要兩個參數(shù),第一個為目標(biāo)節(jié)點, 第二個參數(shù)為需要監(jiān)聽變化的類型,是一個json對象,  實例如下:

       

代碼如下:


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

disconnect方法

disconnect方法會停止觀察目標(biāo)節(jié)點的屬性和節(jié)點變化, 直到下次重新調(diào)用observe方法;

takeRecords

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

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

1:失去滾動, 導(dǎo)致滾動位置不準(zhǔn)確;

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

代碼如下:


/**
* @desc MutationJs, 使用了DOM3的新事件 MutationObserve; 通過監(jiān)聽指定節(jié)點元素, 監(jiān)聽內(nèi)部dom屬性或者dom節(jié)點的更改, 并執(zhí)行相應(yīng)的回調(diào);
* */
window.nono = window.nono || {};
/**
* @desc
* */
nono.MutationJs = function( dom ) {
   //統(tǒng)一兼容問題
   var MutationObserver = this.MutationObserver = window.MutationObserver ||
       window.WebKitMutationObserver ||
       window.MozMutationObserver;
   //判斷瀏覽器是或否支持MutationObserver;
   this.mutationObserverSupport = !!MutationObserver;
   //默認(rèn)監(jiān)聽子元素, 子元素的屬性, 屬性值的改變;
   this.options = {
       'childList': true,
       'subtree': true,
       'attributes' : true,
       'characterData' : true,
       'attributeOldValue' : true,
       'characterDataOldValue' : true
   };
   //這個保存了MutationObserve的實例;
   this.muta = {};
   //list這個變量保存了用戶的操作;
   this.list = [];
   //當(dāng)前回退的索引
   this.index = 0;
   //如果沒有dom的話,就默認(rèn)監(jiān)聽body;
   this.dom = dom|| document.documentElement.body || document.getElementsByTagName("body")[0];
   //馬上開始監(jiān)聽;
   this.observe( );
};
$.extend(nono.MutationJs.prototype, {
   //節(jié)點發(fā)生改變的回調(diào), 要把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, 不準(zhǔn)備處理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發(fā)生改變, 可以統(tǒng)一處理;
           }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();
                   }
               });
           };
       });
       //重新設(shè)置索引;
       this.index = this.list.length-1;
   },
   "removeChildren" : function ( target, nodes ) {
       for(var i= 0, len= nodes.length; i           target.removeChild( nodes[i] );
       };
   },
   "addChildren" : function ( target, nodes ,indexs) {
       for(var i= 0, len= nodes.length; i           if(target.children[ indexs[i] ]) {
               target.insertBefore( nodes[i] , target.children[ indexs[i] ])  ;
           }else{
               target.appendChild( nodes[i] );
           };
       };
   },
   //快捷方法,用來判斷child在父元素的哪個節(jié)點上;
   "indexOf" : function ( target, obj ) {
       return Array.prototype.indexOf.call(target, obj)
   },
   "getIndexs" : function (target, objs) {
       var result = [];
       for(var i=0; i           result.push( this.indexOf(target, objs[i]) );
       };
       return result;
   },
   /**
    * @desc 指定監(jiān)聽的對象
    * */
   "observe" : function( ) {
       if( this.dom.nodeType !== 1) return alert("參數(shù)不對,第一個參數(shù)應(yīng)該為一個dom節(jié)點");
       this.muta = new this.MutationObserver( this.callback.bind(this) );
       //馬上開始監(jiān)聽;
       this.muta.observe( this.dom, this.options );
   },
   /**
    * @desc 重新開始監(jiān)聽;
    * */
   "reObserve" : function () {
       this.muta.observe( this.dom, this.options );
   },
   /**
    *@desc 不記錄dom操作, 所有在這個函數(shù)內(nèi)部的操作不會記錄到undo和redo的列表中;
    * */
   "without" : function ( fn ) {
       this.disconnect();
       fn&fn();
       this.reObserve();
   },
    /**
    * @desc 取消監(jiān)聽;
    * */
    "disconnect" : function () {
       return this.muta.disconnect();
   },
     /**
    * @desc 保存Mutation操作到list;
    * */
   "save" : function ( obj ) {
       if(!obj.undo)return alert("傳進(jìn)來的第一個參數(shù)必須有undo方法才行");
       if(!obj.redo)return alert("傳進(jìn)來的第一個參數(shù)必須有redo方法才行");
       this.list.push(obj);
   },
   /**
    * @desc  ;
    * */
   "reset" : function () {
       //清空數(shù)組;
       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對象, 如果不傳參數(shù)默認(rèn)監(jiān)聽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() 傳一個為函數(shù)的參數(shù), 所有在該函數(shù)內(nèi)部的dom操作, mu不做記錄;

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

代碼如下:


DEMO在IE下的截圖:

如何使用HTML5新特性Mutation Observer實現(xiàn)編輯器的撤銷和回退操作

MutatoinObserver的瀏覽器兼容性:

FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari
Basic support

18

webkit

26

14(14)11156.0WebKit

以上是“如何使用HTML5新特性Mutation Observer實現(xiàn)編輯器的撤銷和回退操作”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!


分享文章:如何使用HTML5新特性MutationObserver實現(xiàn)編輯器的撤銷和回退操作
文章轉(zhuǎn)載:http://weahome.cn/article/ghippj.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部