tinymce 富文本编辑器

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'
本文收录于专栏
收集一些好用的前端开源库,主要是 npm 包