美文网首页
Simple-editor 构成记

Simple-editor 构成记

作者: 旅行着的魔法师 | 来源:发表于2016-09-26 21:32 被阅读0次

    Simple-editor: 基于javascript和css开发的 Web富文本编辑器,轻量、简洁、无依赖。
      Github地址

    <!DOCTYPE HTML>
    <html>
        <head> 
            <meta charset="UTF-8"> 
            <title>Simple-editor demo</title> 
            <!-- css样式文件,webpack加载可忽略 --> 
            <link rel="stylesheet" href="./lib/css/editor.min.css">
        </head>
        <body> 
            <!-- 加载编辑器的容器 --> 
            <div id="container"> 这里写你的初始化内容 </div> 
            <!-- 编辑器源码文件 --> 
            <script type="text/javascript" src="lib/js/editor.min.js"></script> 
            <!-- 实例化编辑器 --> 
            <script type="text/javascript"> 
                var editor = Edit.getEditor('container', {
                    ... // 配置参数 
                }); 
            </script>
        </body>
    </html>
    
    Simple-editor 展示

    因公司业务需求,硬是逼着自己写了个Web端的富文本编辑器,原本用的ueditor,但当页面渲染过多实例后,就显得有些吃力了,毕竟仅仅是压缩过的主文件都有500k,实在有些笨重,再加上项目使用中的一些个性化需求,不得不改动其大片源码,以至于不便于后期迁移和升级维护。
      当然,虽然ueditor近3万行代码,显得很笨重,但其稳建的基础结构还是很值得参考的,所以最终拿了它做主要参考对象。
    </br>
      代码主体有四大模块:Edit.utils(工具)、Edit.ui(UI)、Edit.Editor(实例)和Edit.plugin(拓展),以及最底层的Events(事件)。

    // 底层事件模块
    Events = function(){}
    Events.prototype = {}
    
    // 工具类
    Edit.utils = {}
    
    // UI类
    Edit.ui = {}
    // ui 公用方法
    Edit.ui.Stateful = {}
    // ui Button 构造方法(toolbar工具按钮)
    Edit.ui.Button = function(options){}
    Edit.ui.Button.prototype = {}
    // ui Dialog 构造方法(弹出式选项框)
    Edit.ui.Dialog = function(options){}
    Edit.ui.Dialog.prototype = {}
    // ui Popup 构造方法(次级弹出层)
    Edit.Popup = function(options){}
    Edit.Popup.prototype = {}
    
    // 拓展方法
    Edit.plugin = function(){}
    
    // 编辑器实例
    Edit.Editor = function(opt){}
    Edit.Editor.prototype = {}
    
    // 创建编辑器实例
    Edit.getEdItor = function(){}
    // 销毁编辑器实例
    Edit.delEditor = function(){}
    // 注册UI
    Edit.registerUI = function(){}
    
    // 命令及UI创建
    Edit.ui['bold'] = function(editor){
        editor.commands['bold'] = {
            execCommand: function(){
                this.document.execCommand('bold', flase, null);
            },
            queryCommandState: function(){
                return this.document.queryCommandState('bold');
            }
        };
        var btn = new Edit.ui.Button({
            name: 'bold',
            className: 'eicon-bold',
            title: editor.options.lang['bold'],
            handles: {
                click: function(){
                    editor.execCommand('bold');
                }
            }
        });
        editor.addListener('selectionchange', function(){
            var state = editor.queryCommandState('bold');
            if (!state){
                btn.setChecked(false);
            } else {
                btn.setChecked(true);
            }
        });
        return btn;
    }
    Edit.ui['xxx'] = function(editor){xxx}
    ......
    

    主要结构如上,也对其某些环节进行了改良,比如DOM的字符串拼接改成了js虚拟构建,性能方面得到了提升,而且前端页面展示出来的代码也会特别干净;编辑区域方面,考虑到css的局域污染,暂时采用了iframe的嵌套方式,利用其沙盒机制可以有效防止污染(后续使用中如果发现有性能问题,会考虑去掉这种方式,全部放在当前页面操作);
      编辑命令上ueditor有一套自己的封装,这里并没有采用,而是使用了Web标准的编辑API(兼容性方面或许会有些问题,待检测:不考虑远古浏览器),编辑命令主体上是放在ui回调里面注册的,只有在实例化ui的时候才会去进行注册(根据toolbar的配置去注册其对应命令,通用命令'inserthtml'除外);
    </br>

    // ui 注册
    Edit.registerUI('button', function(editor, uiName) {
        //注册按钮名称对应的command命令 
        editor.registerCommand(uiName, { 
            execCommand: function() { alert('execCommand:' + uiName) } 
        }); 
        //创建一个button 
        var btn = new UE.ui.Button({ 
            //按钮的名字 
            name: uiName, 
            //提示 
            title: uiName, 
            //添加额外样式,直接作用于dom元素的style属性
            stlyle: 'background-image:ulr(xxx.png);background-position: -500px 0;', 
            //事件对象,会将对象集合依次遍历,注册在其对应的dom元素上 
            handles: { 
                click: function() { 
                    // 这里可以不用执行命令,做自己的操作也可 
                    editor.execCommand(uiName); 
                } 
            } 
        }); 
        //当点到编辑内容上时,按钮要做的状态反射 
        editor.addListener('selectionchange', function() { 
            var state = editor.queryCommandState(uiName); 
            if (!state) { 
                btn.setChecked(false); 
            } else { 
                btn.setChecked(state); 
            } 
        }); 
        //因为添加的是button,所以需要返回这个button 
        return btn; 
    });
    

    UI注册基本上延续了ueditor的风格,只是属性值有些变化,回调函数中传入了实例对象,爱干嘛干嘛; 可用此方法来开发编辑器插件,比如plugins目录中的mathtype插件就是用此方法实现。
    </br>

    // 插件方法 拓展
    Edit.plugin.register('autouplod', function(){
        function getPasteImage(e){
            return e.clipboardData && e.clipboardData.items && e.clipboardData.items.length == 1 && /^image\//.test(e.clipboardData.items[0].type) ? e.clipboardData.items:null;
        }
        function getDropImage(e){
            return  e.dataTransfer && e.dataTransfer.files ? e.dataTransfer.files:null;
        }
    
        function sendAndInsertFile(file,editor) {
            var url = editor.options.serverBase64Url;
            if (url) {
                var Form = new FormData();
                var loadingId = 'loading_' + (+new Date()).toString(36);
                Form.append('data', file);
                editor.execCommand('inserthtml', '<img id="'+ loadingId +'" src="/lib/images/loading.gif" style="max-width:100%;height:auto;">');
                
                Edit.Ajax(url,'post',Form,function(cb){
                    var loader = editor.document.getElementById(loadingId);
                    loader.setAttribute('src',cb.data.url);
                    loader.removeAttribute('id');
                    Edit.ui.closePopup();
                });
            } else {
                editor.execCommand('inserthtml', '<img style="max-width:100%;height:auto;" src="'+ file +'">');
            }
        }
    
        return {
                bindEvents:{
                //插入粘贴板的图片,拖放插入图片
                'ready':function(e){
                    var self = this;
                    if(window.FormData && window.FileReader) {
                        self.bind(self.body, 'paste drop', function(e){
                            var hasImg = false,
                                items;
                            //获取粘贴板文件列表或者拖放文件列表
                            items = e.type == 'paste' ? getPasteImage(e):getDropImage(e);
                            if(items){
                                var len = items.length,
                                    file;
                                while (len--){
                                    file = items[len];
                                    if(file.getAsFile) file = file.getAsFile();
                                    if(file && file.size > 0) {                                     
                                        hasImg = true;
                                        var reader = new FileReader();
                                        reader.onload = function (event) {
                                            var base64_str = event.target.result;
                                            sendAndInsertFile(base64_str,self);
                                        }
                                        reader.readAsDataURL(file);  
                                    }
                                }
                                hasImg && e.preventDefault();
                            }
    
                        });
                        //取消拖放图片时出现的文字光标位置提示
                        self.bind(self.body, 'dragover', function (e) {
                            if(e.dataTransfer.types[0] == 'Files') {
                                e.preventDefault();
                            }
                        });
                        
                    }
                }
            }
        }
    });
    

    插件这部分,可以用它来拓展命令和事件,上面展示的是“图片自动粘贴”。
    </br>
      基础介绍如上,详细的实现可能就要去看源码了,虽然有很多现成的Web编辑器可以直接使用,码界也并不提倡重复造轮子,但总是拿来主义也不太好吧,有些坑真的值得去踩一踩、填一填,不然怎好意思自居“开发者”呢,顶多算是个使用者罢了……,当然,更重要的是得满足业务需求。
    </br>
      (目前 Simple-editor 主文件代码1600行,压缩后的min版仅30k;好好学习,天天向上,再见!)

    相关文章

      网友评论

          本文标题:Simple-editor 构成记

          本文链接:https://www.haomeiwen.com/subject/hiwqyttx.html