為富文本添加一個提交按鈕,點擊按鈕可以獲取富文本內容。但是在提交前或者提交后我想做一些操作,比如內容校驗內容清空等等。
我們直接在該按鈕上綁定點擊事件同樣可以達到目的,但是為了組件化,所以本例打算為提交按鈕自定義beforeSubmit、afterSubmit兩個事件。
1.創建發布訂閱者對象
前文說到,事件系統是發布-訂閱模式的一個實現,模式給事件發布函數與事件處理函數進行解耦,使得兩者無直接調用關系。
簡易發布訂閱者對象實現如下:
var Event = { // _cachePool : {}, 處理函數緩存池不要寫在該對象內,因為原型鏈繼承會讓子類共享引用類型數據。 fire : function(type, msg) { var fns; if(!(fns = this._cachePool[type])) { return; } var len = fns.length; for(var i = 0, fn; i < len; i++) { fn = fns[i]; fn.call(this, msg); } }, on : function(type, callback) { if(!this._cachePool[type]) { this._cachePool[type] = []; } this._cachePool[type].push(callback); }, un : function(type, callback) { var fns = this._cachePool[type]; if(fns) { this._cachePool[type] = fns.filter(function(item){//注意兼容,一些低版本Array沒有過濾函數 return item !== callback; }); } } };
這里要去掉緩存池,因為這個對象將是編輯器原型中的一份子,我們希望每個編輯器都有自己一個私有的緩存池,以避免不同編輯器實例監聽到相同的事件。
2.編輯器樣式
本例編輯器給命令按鈕設置圖片樣式,並且僅實現字體加粗功能:
.editorOut { border:1px solid #ccc; width:200px; height:200px; position:relative; } .editorTool { margin:2px; width:100%; } .editorFoot { margin:2px; width:100%; position:absolute; bottom:0; } .editorOut button { height:20px; width:20px; margin:1px; border:0px solid #fff; cursor:pointer; overflow:hidden; } .editorOut button.bold{ background:url(./imgs.svg) 0px 0px; background-size:140px 40px; } .editorOut button.bold:hover { background:url(./imgs.svg) 0px -20px; background-size:140px 40px; }
注:SVG是繪制矢量圖的標簽(XML)語言,它的特點是不受像素影響。
3.實現簡易編輯器
利用js腳本,動態創建編輯器。
_create函數創建編輯器,它封裝了創建編輯器命令塊、底部塊、輸入域等創建的過程。
_createEditor為創建編輯器輸入域函數。
_createTool為創建命令塊函數。
_createCommandBtn為創建調用編輯器命令的按鈕的函數,本例只實現一個按鈕。
_createFoot創建底部塊函數。
_onbeforeSubmit為啟動自定義事件beforesubmit的函數。
_onaftersubmit為啟動自定義事件aftersubmit的函數。
_createPrintBtn為創建輸出按鈕的函數。本例兩個自定義事件都在這里啟動。
EditorProto = { _create : function(id) { var editor = this._createEditor(id); var tool = this._createTool(); var button = this._createCommandBtn(); var foot = this._createFoot(); var printBtn = this._createPrintBtn(); this.printBtn = printBtn; tool.appendChild(button); foot.appendChild(printBtn); editor.insertBefore(tool, editor.firstChild); editor.appendChild(foot); return editor; }, _createEditor : function(id) { var editor = document.getElementById(id); editor.className = "editorOut"; editor.contentEditable = true; return editor; }, _createTool : function() { var tool = document.createElement("div"); tool.contentEditable = false; tool.className = "editorTool"; return tool; }, _createCommandBtn : function() { var button = document.createElement("button"); button.className = "bold"; button.title = "加粗"; button.onclick = function(e) { document.execCommand("bold", false, null); }; return button; }, _createFoot : function() { var tool = document.createElement("div"); tool.contentEditable = false; tool.className = "editorFoot"; return tool; }, _onbeforeSubmit : function(e, scope, callback) { scope.fire('beforesubmit', e); }, _onaftersubmit : function(e, scope, callback) { scope.fire('aftersubmit', e); }, _createPrintBtn : function() { var button = document.createElement("input"), that = this; button.type="button" button.value = "點擊寫出"; button.onmousedown = function(e) { that._onbeforeSubmit(e, that); }; button.onmouseup = function(e) { that._onaftersubmit(e, that); }; return button; } };
4.實現簡易拷貝函數
拷貝函數的作用是將Event對象與EditorProto對象糅合到一起。
var copyIf = function(sub, sup, config) { for(var p in sup) { if(!sub[p]) { sub[p] = sup[p];//拷貝 } } }
5.編輯器組件化
為了達到最終new一次創建一個實例的目的,我們需要將編輯器對象加工成構造函數。像ExtJS、JQuery、Mini UI等都有實現自己的extend函數,本例就不單獨實現繼承函數了。
copyIf(EditorProto, Event);//將Event對象拷貝給EditorProto function Editor(id) { this._cachePool = {};//事件處理函數的緩存池不能放到原型里 this._create(id);//new 操作構造器的時候,創建編輯器實例 }; Editor.prototype = EditorProto;//原型鏈繼承
6.驗證beforesubmit與aftersubmit事件效果
HTML里定義兩個div,做為兩個編輯器的容器。
<div id="editor">print...</div> <div id="s"></div>
JS里分別在HTML兩個div內創建編輯器,並且第一個編輯器綁定兩個自定義事件,第二個只綁定一個,測試效果。
var editor = new Editor("editor"); editor.on("beforesubmit", function() {console.log("beforesubmit");}); editor.on("aftersubmit", function() {console.log("aftersubmit");}); var s = new Editor("s"); s.on("beforesubmit", function() {console.log("s的提交前事件");});
點擊第一個編輯器會打印出“beforesubmit”與“aftersubmit”;點擊第二個編輯器打印出“s的提交前事件”。
為編輯器創建自定義事件目的達成!