tinymce 是最流行的富文本编辑器之一。注:本文章基于 5.x 版本编写。
安装
https://www.tiny.cloud/docs/tinymce/latest/installation/
在最新文档的 installation 中,介绍了各种安装方式。通过 npm 引入且不加载 tiny 云资源的,查看 Self-hosted 部分。
比如将 npm 包中的资源通过 import 引入,捆绑到项目中:
https://www.tiny.cloud/docs/tinymce/latest/react-pm-bundle/
完整开源功能 demo
见文档 Examples - General examples - Full featured demo: Non-Premium Plugins only 中的效果:
https://www.tiny.cloud/docs/tinymce/latest/full-featured-open-source-demo/
webpack 引入方式
https://github.com/tinymce/tinymce/issues/2836 It works with TinyMCE5 and webpack4.:
- raw-loader 引入 content 样式,读取为纯文本,写入到 content_style 字段中
- 引用的组件,需要单独 import 引入;有些是基础功能的组件,如 paste ,如果不引入,编辑器功能会有异常,比如复制会带一堆不需要的样式;
// Import TinyMCE
import "tinymce/tinymce";
// A theme is also required
import "tinymce/themes/silver";
import "tinymce/skins/ui/oxide/skin.css";
// Any plugins you want to use has to be imported
import "tinymce/plugins/advlist";
import "tinymce/plugins/autolink";
import "tinymce/plugins/lists";
import "tinymce/plugins/link";
import "tinymce/plugins/image";
import "tinymce/plugins/charmap";
import "tinymce/plugins/print";
import "tinymce/plugins/preview";
import "tinymce/plugins/anchor";
import "tinymce/plugins/searchreplace";
import "tinymce/plugins/visualblocks";
import "tinymce/plugins/code";
import "tinymce/plugins/fullscreen";
import "tinymce/plugins/insertdatetime";
import "tinymce/plugins/media";
import "tinymce/plugins/table";
import "tinymce/plugins/paste";
import "tinymce/plugins/help";
import "tinymce/plugins/wordcount";
import contentStyle from "!!raw-loader!tinymce/skins/ui/oxide/content.css";
import contentStyle2 from "!!raw-loader!tinymce/skins/content/default/content.css";
tinymce.init({
skin: false,
content_css: false,
content_style: contentStyle.toString() + "\n" + contentStyle2.toString(),
})
自定义 toolbar 按钮
https://www.tiny.cloud/docs/demo/custom-toolbar-button/
在 setup 生命周期中定义 toolbar 按钮和行为; editor 拿到编辑器实例;
tinymce.init({
selector: "textarea#custom-toolbar-button",
toolbar: "customInsertButton customDateButton",
setup: function (editor) {},
content_style:
"body { font-family:Helvetica,Arial,sans-serif; font-size:14px }",
});
定义按钮:
editor.ui.registry.addButton("customInsertButton", {
text: "My Button",
onAction: function (_) {
editor.insertContent(" <strong>It's my button!</strong> ");
},
});
定义功能:
var toTimeHtml = function (date) {
return (
'<time datetime="' +
date.toString() +
'">' +
date.toDateString() +
"</time>"
);
};
editor.ui.registry.addButton("customDateButton", {
icon: "insert-time",
tooltip: "Insert Current Date",
disabled: true,
onAction: function (_) {
editor.insertContent(toTimeHtml(new Date()));
},
onSetup: function (buttonApi) {
var editorEventCallback = function (eventApi) {
buttonApi.setDisabled(eventApi.element.nodeName.toLowerCase() === "time");
};
editor.on("NodeChange", editorEventCallback);
/* onSetup should always return the unbind handlers */
return function (buttonApi) {
editor.off("NodeChange", editorEventCallback);
};
},
});
通过控制按钮的方式插入自定义的内容,其实也够用了,比如插入视频的按钮,如果不合适删掉重新插入就好了。如果需要可以二次修改的功能,可以参考这个案例 Conditional blocks (tinymce.github.io) ,结合了 web components 实现自定义标签的插入和二次编辑。
结合 React 创建及渲染自定义功能
以上的自定义按钮示例都是使用 tinymce 自带的定义方式,代码比较复杂,也不太好维护。
其实可以结合 React 和 Vue 等框架来做,如新建自定义的按钮,点击按钮后弹出 React 控制的窗口,进行编辑与处理后,再关闭窗口并通过 editor.insertContent
插入所需的内容。
如果需要渲染的内容很复杂,可以给插入的内容一个占位符,在编辑器中展示,给一个特殊的类名属性值写在 data-xxx
这种属性里。如果需要渲染自定义的内容,先通过正常 innerHTML 渲染后,再通过 dom 查询找到这些特殊标签,并提取其属性,再将每个特殊区块通过 React.render
渲染为 React 组件。
调用命令
https://www.tiny.cloud/docs/advanced/editor-command-identifiers/
拿到激活的编辑器实例,执行命令
tinymce.activeEditor.execCommand("Bold");
tinymce.activeEditor.execCommand("Italic");
粘贴时附带 word/html 样式
https://www.tiny.cloud/docs/plugins/opensource/paste/
paste 插件的功能
- paste_webkit_styles 从网页中拷贝的样式
- paste_retain_style_properties 从 MS Word 和类似 Office 中拷贝的样式
- 如果希望保留所有样式,可填写 "all"
tinymce.init({
selector: "textarea", // change this value according to your HTML
plugins: "paste",
menubar: "edit",
toolbar: "paste",
paste_webkit_styles: "color font-size",
paste_retain_style_properties: "color font-size",
});
一个粘贴兼容较好的设置(缺点是产生的内容会比较大):
paste_retain_style_properties: 'all',
paste_word_valid_elements: "*[*]", // 配置 word 特定的有效元素
paste_convert_word_fake_lists: false,// 粘贴转换字假列表
paste_webkit_styles: 'all',
paste_merge_formats: true, // 粘贴合并格式
paste_auto_cleanup_on_paste: false,
阻止某些标签
如出于安全目的,禁止粘贴 iframe ,可以使用:
invalid_elements: 'iframe'