tinymce系列(三) tinymce 常用API介绍

1/8/2022 tinymce富文本

# tinymce 常用 API 介绍

这篇文章将会介绍常用的 API,对于下面的示例代码,很多都用到了一个 editor 的变量。 注意 editor 是当前的实例,而非全局变量,如需要使用全局变量改用 tinymce.activeEditor
editor 通常来自 PluginManager.add 注册插件后,回调参数中会携带 editor 实例

# 引入模块

tinymce.util.Tools.resolve

参考代码节选自 :tinymce 的 fullpage 插件 (opens new window)

var global = tinymce.util.Tools.resolve('tinymce.PluginManager')
var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools')
var global$2 = tinymce.util.Tools.resolve('tinymce.html.DomParser')
var global$3 = tinymce.util.Tools.resolve('tinymce.html.Node')
var global$4 = tinymce.util.Tools.resolve('tinymce.html.Serializer')
1
2
3
4
5

如果不使用 util.Tools.resolve 引入,部分功能也可以通过 tinymce.PluginManager 直接调用


# 注册插件

PluginManager.add('插件名称', function(editor,path){})

参数 作用
editor 获取当前 tinymce 实例
path 获取当前加载的 URL(是相对 tinymce 的网络路径,主要用于加载其他额外资源的时候会用到)

* editor 对象作为当前 tinymce 的实例非常重要,包括当前插件中的事件绑定,获取当前实例的配置等

* editor 等同于 tinymce.activeEditortinymce.get('my_editor')

官方示例如下,在 add 的回调方法中进行按钮注册,逻辑绑定等处理
除了基础按钮,下拉菜单,还有非常多的内置表单类型,详情查看文档 tinymce.editor.ui.registry (opens new window)

tinymce.PluginManager.add('MyPlugin', function(editor, url) {
  // Register a toolbar button that triggers an alert when clicked
  // To show this button in the editor, include it in the toolbar setting
  editor.ui.registry.addButton('myCustomToolbarButton', {
    text: 'My Custom Button',
    onAction: function() {
      alert('Button clicked!')
    }
  })

  // Register a menu item that triggers an alert when clicked
  // To show this menu item in the editor, include it in the menu setting
  editor.ui.registry.addMenuItem('myCustomMenuItem', {
    text: 'My Custom Menu Item',
    onAction: function() {
      alert('Menu item clicked')
    }
  })

  // Either return plugin metadata or do not return
  return {
    name: 'MyPlugin',
    url: 'https://mydocs.com/myplugin'
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 获取编辑器传入的参数

editor.getParam

getParma 接收 2 个参数

  1. 要获取的配置变量名
  2. 获取不到的时候的默认值

参数传递在 tinymce.init 方法中开始传入,比如传入一个 myvalue 参数:

业务代码中

tinymce.init({
  selector: 'textarea.tinymce',
  myvalue: { key: 'value' } // 这里的参数是任意类型,可以为string、回调函数等(回调函数建议通过事件的方式触发)
})
1
2
3
4

插件逻辑中,使用 getParam 获取参数

// 如果要使用 editor 必须在 tinymce.PluginManager.add 回调里面拿到 editor 实例
var someval = editor.getParam('myvalue', { key: '1' })

// 如果想在其他地方获取参数,可以使用 tinymce 当前获取焦点的编辑器获取实例
var someval1 = tinymce.activeEditor.getParam('myvalue', { key: '1' })

// 也可以通过指定 tinymce 的节点ID获取 (textarea.tinymce 就是 class名为 tinymce的textarea 标签)
var someval2 = tinymce.get('textarea.tinymce').getParam('myvalue')
1
2
3
4
5
6
7
8

# 事件监听与派发

editor.fire() 和 editor.on()

派发后的事件在业务逻辑中,或者不同的插件中也能监听到,只能能获取 editor 实例的地方都能监听

事件派发:通过 editor.fire('事件名',参数值)
事件监听:事件派发后,通过 editor.on('事件名', 回调函数) 进行监听

实现局部的事件派发:

参考使用 tinymce.util.eventdispatcher (opens new window)

var eventDispatcher = new EventDispatcher()
eventDispatcher.on('click', function() {
  console.log('data')
})
eventDispatcher.fire('click', { data: 123 })
1
2
3
4
5

# tinymce 内置请求

建议还是通过业务逻辑完成请求,避免在插件中发起请求

JSON 请求

var json = new tinymce.util.JSONRequest({
  url: 'somebackend.php',
  params: ['a', 'b'],
  success: function(result) {
    console.dir(result)
  }
})
1
2
3
4
5
6
7

XHR 请求

// Sends a low level Ajax request
tinymce.util.XHR.send({
  url: 'someurl',
  success: function(text) {
    console.debug(text)
  }
})

// Add custom header to XHR request
tinymce.util.XHR.on('beforeSend', function(e) {
  e.xhr.setRequestHeader('X-Requested-With', 'Something')
})
1
2
3
4
5
6
7
8
9
10
11
12

JSONP 请求 在文档没有详细列出,在 tinymce.js 源码可以看到相关的逻辑

JSONP 虽然是通过添加 script 标签发出的请求,不过会在底层参数中加上对应的资源标识,所以可以使用 callback 找到发出的请求对应的参数值

tinymce JSONP 源码实现部分:

var JSONP = {
  callbacks: {},
  count: 0,
  send: function(settings) {
    var self = this,
      dom = DOMUtils$1.DOM,
      count = settings.count !== undefined ? settings.count : self.count
    var id = 'tinymce_jsonp_' + count
    self.callbacks[count] = function(json) {
      dom.remove(id)
      delete self.callbacks[count]
      settings.callback(json)
    }
    dom.add(dom.doc.body, 'script', {
      id: id,
      src: settings.url,
      type: 'text/javascript'
    })
    self.count++
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

JSONP 在插件中使用,发送请求

const $jsonp = tinymce.util.Tools.resolve('tinymce.util.JSONP')

$jsonp.send({
  url: 'someurl',
  callback: function(json) {
    resolve(json)
  }
})
1
2
3
4
5
6
7
8

# 动态添加资源

有时候会有动态加载 JS,或者动态加载样式的需求

tinymce 有时候是通过 iframe 的形式嵌入,直接使用 document.body 插入的资源也无法影响 iframe 内容,所以需要使用内置的 API 进行添加

动态加载 JS tinymce.dom.scriptloader (opens new window)

// Load a script from a specific URL using the global script loader
tinymce.ScriptLoader.load('somescript.js')

// Load a script using a unique instance of the script loader
var scriptLoader = new tinymce.dom.ScriptLoader()

scriptLoader.load('somescript.js')

// Load multiple scripts
var scriptLoader = new tinymce.dom.ScriptLoader()

scriptLoader.add('somescript1.js')
scriptLoader.add('somescript2.js')
scriptLoader.add('somescript3.js')

scriptLoader.loadQueue(function() {
  alert('All scripts are now loaded.')
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

通过添加 dom 节点方式添加 tinymce.dom.domutils (opens new window)

tinymce.activeEditor.dom.add(tinymce.activeEditor.getBody(), 'script', {
  src: 'somescript4.js',
  type: 'text/javascript'
})
1
2
3
4

# 获取和操作 dom 节点 和 操作 html

操作 dom 的时候尽可能使用内置的 DomQuery 等之类的 API,因为有时候 tinymce 是通过 iframe 的形式嵌入,普通的 document.querySelector 可能无法找到对应的 dom,插入的资源也无法影响 iframe 内容

如果要使用 document.querySelector 等原生 API,使用 tinymce.activeEditor.getBody() 或 editor.doc 替换 document

editor.doc.querySelector('.my_plugin') // 获取到编辑器中 .my_plugin 的节点
1

tinymce.activeEditor.getBody() 或 editor.doc 永远都指向于 tinymce 的 dom 实例

  • 获取 dom 节点,更多适用于已有 dom 节点,需要操作节点中的内容

tinymce.dom.DomQuery (opens new window)

tinymce 提供了类似 JQ 的功能。不过没有 JQ 那么完善,使用方法可以直接通过 $ = tinymce.dom.DomQuery 或者使用 $ = tinymce.util.Tools.resolve('tinymce.dom.DomQuery')


  • html 操作,更多适用于字符串类型解析为 dom 类型

把 HTML 字符串解析 html 节点tinymce.html.domparser (opens new window)

把 HTML 解析单个节点 tinymce.html.node (opens new window)

# 获取光标选中内容

选中的文本会有 2 个情况,在代码的注释有说明。代码节选中 zk_quick_style 部分

相关 API 文档:

光标操作: tinymce.dom.selection (opens new window)

功能是把选中的文本/没选中状态下把该段文本包裹一个指定的样式

/**
 * 获取选中的内容
 * @param {*} editor
 */
function getRangeText(editor = tinymce.activeEditor) {
  let rngInfo = editor.selection.getRng()
  let rangeText = rngInfo.commonAncestorContainer.textContent
  if (!rangeText) {
    return ''
  }

  // 这里是因为如果有3段文本,选中其中2段的时候,commonAncestorContainer 的文本其实是给出了3段合并的文字
  // 但是选中范围是根据第一段文字和最后一段文字来计算的位置,所以要根据开头文本开始匹配
  let _startText = rngInfo.startContainer.textContent
  rangeText = rangeText.replace(new RegExp(`.*(${_startText}.*)`), '$1')

  let _start = rngInfo.startOffset
  let _end = rngInfo.endOffset
  let _select = _end - _start

  if (_select == 0 || _select == rangeText.length) {
    // 情况1:_select == 0 就是没有选中任何文本,那就把当前的 div 的文本都提出来,走默认就行,并且创建选取,选中该文本
    // 情况2:在同一行中,选中了全部的字,这时候应该把他上级的节点也选中,否则使用 insert 的时候上面会多一个空行
    editor.selection.select(rngInfo.commonAncestorContainer)
  } else {
    // 如果有选中内容,并且考虑多行的情况, _end 记录的是最后一行选中的字符,而不是光标全中的长度
    let _endText = rngInfo.endContainer.textContent
    rangeText = rangeText.replace(new RegExp(`(.*)${_endText}`), '$1')
    rangeText += _endText.substring(0, _end)
    rangeText = rangeText.substring(_start, rangeText.length)
  }
  return rangeText
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 保存快照(撤销和重做)

原文文档:tinymce.undomanager (opens new window)

在进行一些操作步骤后如果想记录该操作用用户可以撤销的话,可以使用以下指令

editor.undoManager.add()
1

# 执行内置指令

指令文档 execcommand (opens new window)

web 中也有内置指令 MDN-execCommand (opens new window)不过该 API 已经废弃,虽然可能还能调用(不清楚兼容性),推荐还是使用 tinymce 封装过的 execCommand 进行指令执行

对于常见的 加粗,居中 等操作。web 有一套指令,tinymce 在这基础上也封装了独有的指令,调用方式如下:

// 示例
editor.execCommand('指令名称')

// 左对齐指令
editor.execCommand('JustifyLeft')

// 居中
editor.execCommand('JustifyCenter')
1
2
3
4
5
6
7
8

收集到的常用指令如下:

指令名称 效果
bold 加粗
italic 斜体
subscript 下标
superscript 上标
strikeThrough 删除线
underline 下划线
insertOrderedList 插入有序列表
insertUnorderedList 插入无序列表
justifyCenter 居中
justifyFull 环绕对齐
justifyLeft 左对齐
justifyRight 右对齐
insertHorizontalRule 插入水平尺

文档没有列出很多的资料名称,不过可以通过查阅源码找到想要执行的指令,在 tinymce.js 文件中搜索 EditorCommands.prototype.setupCommands 可以看到大多数的指令和实现方式

# 注册自己的指令

相关文档: addcommand (opens new window)

代码来自官网的示例:

注册了一个叫 mycommand 的指令。效果是通过 内置的 windowManager 弹出 当前选中的文本

editor.addCommand('mycommand', function(ui, v) {
  editor.windowManager.alert('Hello world!! Selection: ' + editor.selection.getContent({ format: 'text' }))
})

// 执行指令
editor.execCommand('mycommand')
1
2
3
4
5
6

# 插入和设置内容

分别是 setContentinsertContent

editor.setContent(`<p>把整个编辑器内容设置为这段文本</p>`)
editor.insertContent(`<p>在光标当前位置插入当前文本</p>`)
1
2

# 最后

tinymce 常用的 API 大概就是以上的内容,已经涵盖了事件监听,获取 dom 节点,光标操作,内部指令等。

光有 API 还是不太够,下一章将会介绍 tinymce 常用内置 UI 组件介绍

Last Updated: 1/7/2024, 5:51:59 PM