實現一個簡易的富文本編輯器(二):給富文本添加自定義事件


  為富文本添加一個提交按鈕,點擊按鈕可以獲取富文本內容。但是在提交前或者提交后我想做一些操作,比如內容校驗內容清空等等。

  我們直接在該按鈕上綁定點擊事件同樣可以達到目的,但是為了組件化,所以本例打算為提交按鈕自定義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的提交前事件”。

  為編輯器創建自定義事件目的達成!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM