首頁 web前端 H5教程 基於HTML5新特性Mutation Observer實作編輯器的撤銷與回退操作_html5教學技巧

基於HTML5新特性Mutation Observer實作編輯器的撤銷與回退操作_html5教學技巧

May 16, 2016 pm 03:46 PM
撤銷 編輯器

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:第二個參數則是這個觀察者物件本身.

比如這樣:

     

複製程式碼
程式碼如下:


程式碼如下:



程式碼如下:

程式碼如下:

程式碼如下:

程式碼如下: mutations.forEach(function(mutation) { console.log(mutation.type);

});

});


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


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

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

       

複製程式碼

程式碼

'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,
'characterDataOld'Value : 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({ _this.disconnect();
_this.addChildren(target, record.removedNodes ,indexs );
_this.reObserve();
}, _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.removeChild(target, re. addedNodes );
_this.reObserve();
},
"redo" : function () {
_this.disconnect();
_this.addldldren(target, record. indexs);
_this.reObserve();
}
});
};
//@desc characterData是什麼鬼;
//ref : http:// baike.baidu.com/link?url=Z3Xr2y7zIF50bjXDFpSlQ0PiaUPVZhQJO7SaMCJXWHxD6loRcf_TVx1vsG74WUSZ_0-7wq4_oq0Ci-8ghUAG8a 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({
"실행 취소" : 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如何使사용呢?


复代码
代码如下:

//這個是實例化一個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,Google瀏覽器,IE11上面運作完全正常:


複製代碼
代碼如下:

br />











MutationObserver是為了取代原來Mutation Events的一系列事件, 瀏覽器會監聽指定Element下所有元素的新增,刪除,替換等;



;
;

;


div>
<script><br /> window.onload = function () {<br /> window.mu = new nono.MutationJs();<br /> //取消監聽<br /> mu.disconnect();<br /> //取消監聽<br /> mu.disconnect();<br /> //取消監聽<br /> mu.disconnect();<br /> //重新監聽<br /> mu.reObserve();<br /> document.getElementById("b0").addEventListener("click", function ( ev ) {<br /> div = document.createElement("""""""" );<br /> div.innerHTML = document.getElementById("value").value;<br /> document.getElementById("div").appendChild( div );<br /> });<br /> doc "prev").addEventListener("click", function ( ev ) {<br /> mu.undo();<br /> });<br /> document.getElementById("next").addEventListenerener("click", function ( ev ) {<br /> mu.redo();</script>
}); };
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1664
14
CakePHP 教程
1421
52
Laravel 教程
1315
25
PHP教程
1266
29
C# 教程
1239
24
如何解決 Windows 11 中的檔案名稱或副檔名過長的問題? 如何解決 Windows 11 中的檔案名稱或副檔名過長的問題? Apr 22, 2023 pm 04:37 PM

您在傳輸文件時是否遇到任何問題,並且禁止您這樣做?好吧,許多Windows用戶最近報告說,他們在將檔案複製並貼上到資料夾時遇到了問題,其中拋出了一個錯誤,提示「目標資料夾的檔案名稱太長」。此外,其他一些Windows用戶在打開任何文件時表示失望,並說“文件名或擴展名太長”,他們無法打開文件。這不允許他們將文件傳輸到任何其他資料夾,這讓用戶感到失望。在分析問題時,我們提出了一系列解決方案,可能有助於緩解問題,使用者可以輕鬆傳輸檔案。如果您也遇到類似情況,請參閱此貼文以了解更多資訊。來源:https

15 款 Python 編輯器/ IDE 細緻攻略,總有一款適合你! 15 款 Python 編輯器/ IDE 細緻攻略,總有一款適合你! Aug 09, 2023 pm 05:44 PM

寫 Python 程式碼最好的方式莫過於使用整合開發環境(IDE)了。它們不僅能讓你的工作更加簡單、更具邏輯性,還能夠提升程式設計體驗和效率。每個人都知道這一點。而問題在於,如何從眾多選項中選擇最佳的 Python 開發環境。

如何在Windows 11、10中關閉Windows Defender智慧畫面? 如何在Windows 11、10中關閉Windows Defender智慧畫面? Apr 26, 2023 am 11:46 AM

許多Windows用戶最近報告說,當WindowsDefenderSmartScreen警告用戶不要啟動MicrosoftWindows無法識別的應用程式時,他們感到惱火,他們每次都必須點擊「仍然運行」選項。 Windows用戶不確定他們目前可以做些什麼來避免或停用它。在研究了這個問題後,我們發現系統上的WindowsDefender功能可以透過設定應用程式或本機群組原則編輯器或透過調整登錄檔來停用。透過這樣做,使用者將不再需要面對防守者SmartScreen。如果您的系統也遇到

C語言程式設計必備軟體:五個推薦給初學者的好幫手 C語言程式設計必備軟體:五個推薦給初學者的好幫手 Feb 20, 2024 pm 08:18 PM

C語言作為一門基礎而重要的程式語言,對於初學者來說,選擇合適的程式設計軟體是非常重要的。在市面上有許多不同的C語言程式設計軟體可供選擇,但對於初學者來說,適合自己的選擇可能有些困惑。本文將推薦給初學者的五個C語言程式設計軟體,幫助他們快速入門並提升程式設計能力。 Dev-C++Dev-C++是一款免費開源的整合開發環境(IDE),特別適合初學者使用。它簡單易用,整合了編輯器、

修正 Windows 11/10 登入選項已停用的問題 修正 Windows 11/10 登入選項已停用的問題 May 07, 2023 pm 01:10 PM

許多Windows使用者都曾經遇到由於登入嘗試失敗或多次關閉系統而無法登入Windows11/10系統的問題。用戶感到沮喪,因為他們對此無能為力。使用者可能忘記了登入系統的PIN碼,或是使用或安裝軟體時出現卡頓,系統可能被多次強制關閉。因此,我們制定了一份最好的可用解決方案列表,這些解決方案無疑將幫助消費者解決這個問題。要了解更多信息,請繼續閱讀本文。注意:在此之前,請確保您擁有系統的管理員憑證和Microsoft帳戶密碼以重設PIN。如果沒有,請等待一個小時左右,然後嘗試使用正確的PIN

如何使用 ClipChamp:免費的 Windows 11 影片編輯器 如何使用 ClipChamp:免費的 Windows 11 影片編輯器 Apr 20, 2023 am 11:55 AM

還記得Windows7上的WindowsMo​​vieMaker嗎?自從停止WindowsMo​​vieMaker以來,微軟還沒有推出任何真正的電影製作者。另一方面,他們嘗試用一個小巧輕便的內建影片編輯器來改造照片應用程式。很長一段時間後,微軟推出了Clipchamp,這是一款適用於所有Windows11裝置的更好的視訊處理器。在本文中,我們將深入探討如何從Windows11裝置上的Clipchamp應用程式中取得所有內容。如何使用Clipchamp–詳細教學我們提供

如何在 Windows 11 和 10 上使用 Clipchamp 影片編輯器 如何在 Windows 11 和 10 上使用 Clipchamp 影片編輯器 Apr 17, 2023 pm 07:55 PM

如何在Windows上安裝和使用ClipchampClipchamp應用程式尚未預先安裝在Windows上,但這是未來的計畫。同時,您需要先下載並安裝Clipchamp。要在Windows11和Windows10上安裝和使用Clipchamp:從MicrosoftStore下載並安裝Clipchamp。安裝後,在開始功能表中搜尋Clipchamp以啟動它。在Clipchamp視窗中,您需要使用您的Microsoft或Google帳戶登錄,或使用您自己的個人電子郵件

在每個 Word 編輯器中使用的 10 個刪除線快捷方式 在每個 Word 編輯器中使用的 10 個刪除線快捷方式 Apr 16, 2023 pm 05:25 PM

文字編輯器,也稱為文字處理器,可以定義為允許您建立、列印和編輯文件的裝置或軟體。您可以鍵入內容、將其顯示在螢幕或列印材料上、以電子方式存儲,並使用不同的鍵盤快捷鍵、字元和命令從鍵盤進行修改,包括用於刪除線的鍵盤快捷鍵。計算機的製造是為了幫助解決不同的問題。但是,文字處理是他們幫助的最受歡迎的功能。由於技術進步,您可以將文字編輯器作為安裝在行動裝置和電腦上的軟體應用程式或作為不同供應商提供的雲端服務存取。文字處理器於1960年代初作為類似電動打字機的獨立機器首次推出。它們比打字機更好,因為它們允

See all articles