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

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
理解H5:含義和意義理解H5:含義和意義May 11, 2025 am 12:19 AM

H5是HTML5,是HTML的第五個版本。 HTML5提升了網頁的表現力和交互性,引入了語義化標籤、多媒體支持、離線存儲和Canvas繪圖等新特性,推動了Web技術的發展。

H5:可訪問性和網絡標準合規性H5:可訪問性和網絡標準合規性May 10, 2025 am 12:21 AM

無障礙訪問和網絡標準遵循對網站至關重要。 1)無障礙訪問確保所有用戶都能平等訪問網站,2)網絡標準遵循提高網站的可訪問性和一致性,3)實現無障礙訪問需使用語義化HTML、鍵盤導航、顏色對比度和替代文本,4)遵循這些原則不僅是道德和法律要求,還能擴大用戶群體。

HTML中的H5標籤是什麼?HTML中的H5標籤是什麼?May 09, 2025 am 12:11 AM

HTML中的H5標籤是第五級標題,用於標記較小的標題或子標題。 1)H5標籤幫助細化內容層次,提升可讀性和SEO。 2)結合CSS可定製樣式,增強視覺效果。 3)合理使用H5標籤,避免濫用,確保內容結構邏輯性。

H5代碼:Web結構的初學者指南H5代碼:Web結構的初學者指南May 08, 2025 am 12:15 AM

HTML5構建網站的方法包括:1.使用語義化標籤定義網頁結構,如、、等;2.嵌入多媒體內容,使用和標籤;3.應用表單驗證和本地存儲等高級功能。通過這些步驟,你可以創建一個結構清晰、功能豐富的現代網頁。

H5代碼結構:組織內容以實現可讀性H5代碼結構:組織內容以實現可讀性May 07, 2025 am 12:06 AM

通過合理的H5代碼結構可以讓頁面在眾多內容中脫穎而出。 1)使用語義化標籤如、、等組織內容,使結構清晰。 2)通過CSS佈局如Flexbox或Grid控制頁面在不同設備上的呈現效果。 3)實現響應式設計,確保頁面在不同屏幕尺寸上自適應。

H5與較舊的HTML版本:比較H5與較舊的HTML版本:比較May 06, 2025 am 12:09 AM

HTML5(H5)與舊版本HTML的主要區別包括:1)H5引入了語義化標籤,2)支持多媒體內容,3)提供離線存儲功能。 H5通過新標籤和API增強了網頁的功能和表現力,如和標籤,提高了用戶體驗和SEO效果,但需注意兼容性問題。

H5與HTML5:澄清術語和關係H5與HTML5:澄清術語和關係May 05, 2025 am 12:02 AM

H5和HTML5的區別在於:1)HTML5是網頁標準,定義結構和內容;2)H5是基於HTML5的移動網頁應用,適用於快速開發和營銷。

HTML5特徵:H5的核心HTML5特徵:H5的核心May 04, 2025 am 12:05 AM

HTML5的核心特性包括語義化標籤、多媒體支持、表單增強和離線存儲與本地存儲。 1.語義化標籤如、等提高了代碼可讀性和SEO效果。 2.多媒體支持通過和標籤簡化了嵌入媒體內容的過程。 3.表單增強引入了新的輸入類型和驗證屬性,簡化了表單開發。 4.離線存儲和本地存儲通過ApplicationCache和localStorage等提高了網頁性能和用戶體驗。

See all articles

熱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

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

熱門文章

熱工具

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能