Summernote 是一个简单,灵活,所见即所得(WYSIWYG)的编辑器,基于 jQuery 和 Bootstrap 构建。Summernote 所有主要的操作都支持快捷键,有一个功能强大的 API,它提供了大量的自定义选项的设计(宽,高,有效的项目等等)和功能。对于主要的脚本语言或框架(PHP,Ruby,Django,NodeJS),该项目有提供了集成示例。
Bootstrap summernote,用其官网上的介绍就是“Super Simple WYSIWYG editor”,不过在我看来,与bootstrap中文官网上提供的“bootstrap-wysiwyg”要更simple,更漂亮,更好用!
虽然我之前尝试过使用bootstrap-wysiwyg,可参照Bootstrap wysiwyg富文本数据如何保存到MysqL,但事后诸葛亮的经验告诉我,summernote绝对是更佳的富文本编辑器,这里对其工作team点三十二个赞!!!!!
经过一天时间的探索,对summernote有所掌握,那么为了更广大前端爱好者提供便利,我将费劲一番心血来介绍一下summernote,超级福利啊。
一、官方API和源码下载
工欲善其事必先利其器,首先把summernote的源码拿到以及对应官方API告诉大家是首个任务!
官网(demo和api)
github源码下载,注意下载开发版
二、效果图
效果图1
三、开讲内容
大的方向为以下三个内容:
summernote的页面布局(资源引入、初始参数)
summernote从本地上传图片方法(前端onImageUpload方法、后端springMVC文件保存)
summernote所在form表单的数据提交
①、summernote的页面布局
502_56@
<
Meta charset="UTF-8">
summernote - bs3fa4
required-validate" action="#" enctype="multipart/form-data" method="post" onsubmit="return iframeCallback(this,pageAjaxDone)">
@H_502_56@html5的标记是必须的,注意千万不能是显示怪怪的,按钮的大小布局不一致,这里就不再上图了,但是千万注意!
bootstrap 的版本号最好为v3.3.5
1、布局div
相信你也看到了我为div加上的三个属性name、placeholder、action,那么我们来详细介绍一下三个属性的作用:
name,为外层form表单提供summernote数据保存时的数据模型的属性名,和input标签的name属性作用一致,稍候在form提交的时候具体介绍。
placeholder,很直白,为summernote提供初始状态的文本描述,当然还需要后续加工,div显然是不支持placeholder属性的。
action,为图片上传提供后端接收地址,稍候在介绍图片上传onImageUpload会再次用到。
另外${deal.description}其实你不需要太多关注,和textarea的赋值的用法一致,就是单纯的显示保存后的内容。
2、summernote初始化
使用jquery获取到页面上的summernote,对其进行初始化,我们来详细介绍列出参数的用法(先不介绍图片上传的onImageUpload 方法)。
lang ,指定语言为中文简体
placeholder ,summernote初始化显示的内容。
minHeight,最小高度为300,注意这里没有使用height,是有原因的,这里稍作解释,就不上图了。当使用height指定高度后,假如上传比height高的图片,summernote就不会自动调整高度,并且前文中“效果图3”中标出的红色区域会不贴着图片,而溢出到summernote外部。
dialogsFade,增加summernote上弹出窗口滑进滑出的动态效果。
dialogsInBody,这个属性也很关键,默认为false,字面上的意思是summernote的弹出框是否在body中(in嘛),设置为false时,dialog的式样会继承其上一级外部(如上文中的form-horizontal)容器式样,那么显示的效果就很别扭,这里也不再上图;那么设置为true时,就不会继承上一级外部div的属性啦,从属于body嘛。
disableDragAndDrop,设置为false吧,有的时候拖拽会出点问题,你可实践。
②、summernote从本地上传图片方法
1、前端onImageUpload方法
假如问度娘如下的话:“onImageUpload方法怎么写?”,度娘大多会为你找到如下回答:
以上资源来自于stackoverflow。
但其实呢,summernote-develop版本的summernote已经不支持这种onImageUpload写法,那么如今的写法是什么样子呢?参照summernote的官网例子。
onImageUpload
那么此时onImageUpload的具体写法呢?(后端为springMVC):
支持选择多张
图片
onImageUpload : function(files) {
var $files = $(files);
// 通过each
方法遍历每一个file
$files.each(function() {
var file = this;
// FormData,新的form表单封装,具体可
百度,但其实
用法很简单,如下
var data = new FormData();
// 将
文件加入到file中,后端可获得到参数名为“file”
data.append("file",file);
// ajax
上传
$.ajax({
data : data,// div上的action
cache : false,// 成功时
调用方法,后端返回json数据
success : function(response) {
// 封装的eval
方法,可
百度
var json = YUNM.jsonEval(response);
// 控制台
输出返回数据
YUNM.debug(json);
// 封装
方法,主要是
显示错误提示信息
YUNM.ajaxDone(json);
// 状态ok时
if (json[YUNM.keys.statusCode] == YUNM.statusCode.ok) {
//
文件不为空
if (json[YUNM.keys.result]) {
//
获取后台数据保存的
图片完整路径
var imageUrl = json[YUNM.keys.result].completeSavePath;
// 插入到summernote
$this.summernote('insertImage',function($image) {
// todo,后续可以对image对象
增加新的css式样等等,这里默认
});
}
}
},// ajax请求失败时处理
error : YUNM.ajaxError
});
});
}
}
注释当中加的很详细,这里把其他关联的代码一并贴出,仅供参照。
" + xhr.responseText + "
");
} else {
$.showErr("
Http status: " + xhr.status + " " + xhr.statusText + "
" + "
ajaxOptions: " + ajaxOptions + "
"
+ "
thrownError: " + thrownError + "
");
}
},ajaxDone : function(json) {
if (json[YUNM.keys.statusCode] == YUNM.statusCode.error) {
if (json[YUNM.keys.message]) {
YUNM.debug(json[YUNM.keys.message]);
$.showErr(json[YUNM.keys.message]);
}
} else if (json[YUNM.keys.statusCode] == YUNM.statusCode.timeout) {
YUNM.debug(json[YUNM.keys.message]);
$.showErr(json[YUNM.keys.message] || YUNM.msg("sessionTimout"),YUNM.loadLogin);
}
},
required-validate" action="#" enctype="multipart/form-data" method="post" onsubmit="return iframeCallback(this,pageAjaxDone)">
文件
上传处理");
var $form = $(form),$iframe = $("#callbackframe");
var data = $form.data('bootstrapValidator');
if (data) {
if (!data.isValid()) {
return false;
}
}
// 富文本编辑器
$("div.summernote",$form).each(function() {
var $this = $(this);
if (!$this.summernote('isEmpty')) {
var editor = "
";
$form.append(editor);
} else {
$.showErr("请填写项目详情");
return false;
}
});
if ($iframe.size() == 0) {
$iframe = $("
").appendTo("body");
}
if (!form.ajax) {
$form.append('
');
}
form.target = "callbackframe";
_iframeResponse($iframe[0],callback || YUNM.ajaxDone);
}
function _iframeResponse(iframe,callback) {
var $iframe = $(iframe),$document = $(document);
$document.trigger("ajaxStart");
$iframe.bind("load",function(event) {
$iframe.unbind("load");
$document.trigger("ajaxStop");
if (iframe.src == "javascript:'%3Chtml%3E%3C/html%3E';" || // For
// Safari
iframe.src == "javascript:'';") { // For FF,IE
return;
}
var doc = iframe.contentDocument || iframe.document;
// fixing Opera 9.26,10.00
if (doc.readyState && doc.readyState != 'complete')
return;
// fixing Opera 9.64
if (doc.body && doc.body.innerHTML == "false")
return;
var response;
if (doc.XMLDocument) {
// response is a xml document Internet Explorer property
response = doc.XMLDocument;
} else if (doc.body) {
try {
response = $iframe.contents().find("body").text();
response = jQuery.parseJSON(response);
} catch (e) { // response is html document or plain text
response = doc.body.innerHTML;
}
} else {
// response is a xml document
response = doc;
}
callback(response);
});
}