tinymce6实现目录功能(包含生成目录大纲和点击跳转)

tinymce6实现目录功能(包含生成目录大纲和点击跳转)

前言

终于最近又回到文档编辑器开发这边了,可以继续给大家更新tinymce的更多经验了。之前是在vue2开发的系统中使用tinymce5编辑器,实现了很多功能没有分享,最近做框架迁移,在vue3搭建的框架中实现之前的功能,同时我把tinymce的版本也升级到了6.x,后面会多多分享。

需求

编辑器的引入和插件的引入在之前的文章有介绍过,如果还没有引入成功的可以去翻翻。
还有一些根据文章依旧没有引入成功的小伙伴,试试以下步骤:
1.首先看一下版本,tinymce5和tinymce6的插件引入方式不一定完全相同,一般建议是将plugin.js和plugin.min.js都加上;
2.然后确定插件引入存放的位置,我是在public文件夹下,保证路径正确,插件引入时的baseURL真的很重要,一开始我也因为baseURL的问题没引入成功;
3.最后看一下报错,如果是“<”之类的字符问题,那就是插件已经读取到了自定义的文件,但是文件中有错误;如果最后的最后还是没有生效,并且没有报错,那就是没有读到文件,那可能是你引入tinymce的地方读取插件的配置不对,可以看一下我前面“如何引入tinymce”的文章,这个插件引入有几种方法有很多细微的差别。

tinymce6将很多原本的插件都写入核心编辑器功能了,比如字体、列表、排版等,不是你的菜单栏有内容就代表引入插件成功的,这个要注意。

目录的实现

这个实现过程我就不多赘述了,就是根据一级二级三级目录生成一个目录信息,即h1、h2、h3等做不同处理;拼装时加入herf,以便增加点击事件跳转;最后将拼装完成的东西再加不可编辑属性,使其在文档中不能被随意编辑,只能更新。代码如下

// plugin.js
; (function () {
  tinymce.PluginManager.add('toc', function (editor) {
    const getTocClass = () => {
      return editor.getParam('toc_class', 'mce-toc');
    };

    const getTocHeader = () => {
      const tagName = editor.getParam('toc_header', 'h2');
      return /^h[1-6]$/i.test(tagName) ? tagName : 'h2';
    };

    const getTocDepth = () => {
      const depth = parseInt(editor.getParam('toc_depth', '3'), 10);
      return depth >= 1 && depth <= 9 ? depth : 3;
    };

    const create = (prefix) => {
      let counter = 0;
      return () => {
        const guid = new Date().getTime().toString(32);
        return prefix + guid + (counter++).toString(32);
      };
    };

    const scrollToElement = (id) => {
      const element = editor.getBody().querySelector(`#${id}`);
      if (element) {
        element.scrollIntoView({ behavior: 'smooth' });
      }
    };

    // 点击事件处理程序,处理目录点击跳转到相应内容
    const handleClick = (e) => {
      e.preventDefault();
      const target = e.target;
      if (target.tagName.toLowerCase() === 'a' && target.hasAttribute('href')) {
        const href = target.getAttribute('href').replace('#', '');
        scrollToElement(href);
      }
    };

    const setupClickEvent = () => {
      const tocClass = getTocClass();
      editor.on('click', (e) => {
        const $tocElm = editor.getBody().querySelector(`.${tocClass}`);
        if ($tocElm && e.target.closest(`.${tocClass}`)) {
          handleClick(e);
        }
      });
    };

    setupClickEvent();

    const tocId = create('mcetoc_');

    const readHeaders = () => {
      const tocClass = getTocClass();
      const headerTag = getTocHeader();
      const selector = `h1, h2, h3, h4, h5, h6`;
      const headers = Array.from(editor.getBody().querySelectorAll(selector));
      if (headers.length && /^h[1-9]$/i.test(headerTag)) {
        return headers.filter((el) => !el.classList.contains(tocClass)).map((h) => {
          const id = h.id ? h.id : tocId();
          return {
            id,
            level: parseInt(h.tagName.replace(/^H/i, ''), 10),
            title: h.textContent,
            element: h,
          };
        });
      }
      return [];
    };

    const generateTocContentHtml = () => {
      const headers = readHeaders();
      const minLevel = headers.reduce((min, h) => Math.min(min, h.level), 9);

      let html = '';
      if (!headers.length) {
        return html;
      }

      html += `<${getTocHeader()} contenteditable="true">${editor.translate('目录')}</${getTocHeader()}>`;
      let prevLevel = minLevel - 1;

      headers.forEach((h) => {
        h.element.id = h.id;
        const nextLevel = headers[headers.indexOf(h) + 1]?.level || null;

        if (prevLevel === h.level) {
          html += '<li>';
        } else {
          for (let i = prevLevel; i < h.level; i++) {
            html += '<ul><li>';
          }
        }

        html += `<a href="#${h.id}">${h.title}</a>`;

        if (nextLevel === h.level || !nextLevel) {
          html += '</li>';
          if (!nextLevel) {
            html += '</ul>';
          }
        } else {
          for (let i = h.level; i > nextLevel; i--) {
            if (i === nextLevel + 1) {
              html += '</li></ul><li>';
            } else {
              html += '</li></ul>';
            }
          }
        }

        prevLevel = h.level;
      });

      return html;
    };

    const insertToc = () => {
      const tocClass = getTocClass();
      const $tocElm = editor.getBody().querySelector(`.${tocClass}`);
      const tocHtml = generateTocContentHtml();

      if (!$tocElm || !$tocElm.textContent) {
        editor.insertContent(`<div class="${tocClass}" contenteditable="false">${tocHtml}</div>`);
      } else {
        updateToc(tocHtml);
      }
    };

    const updateToc = (tocHtml) => {
      const tocClass = getTocClass();
      const $tocElm = editor.getBody().querySelector(`.${tocClass}`);
      if ($tocElm) {
        editor.undoManager.transact(() => {
          $tocElm.innerHTML = tocHtml;
        });
      }
    };

    editor.addCommand('mceInsertToc', () => {
      insertToc();
    });

    editor.addCommand('mceUpdateToc', () => {
      const tocHtml = generateTocContentHtml();
      updateToc(tocHtml);
    });

    editor.ui.registry.addButton('toc', {
      icon: 'toc',
      tooltip: '目录',
      onAction: () => {
        insertToc();
      },
    });

    editor.ui.registry.addButton('tocupdate', {
      icon: 'reload',
      tooltip: 'Update',
      onAction: () => {
        const tocHtml = generateTocContentHtml();
        updateToc(tocHtml);
      },
    });

    editor.ui.registry.addMenuItem('toc', {
      icon: 'toc',
      text: '目录',
      onAction: () => {
        insertToc();
      },
    });

    editor.ui.registry.addContextToolbar('toc', {
      items: 'tocupdate',
      predicate: (node) => node.classList.contains(getTocClass()),
      scope: 'node',
      position: 'node',
    });
  });
})()

tinymce的官方付费版是包含很多功能的,比如目录、评注等,但是价格还挺贵的,好像一个月六百多美元,相信大家都想自己开发自定义插件来省这笔钱,那就关注我,接下来我会更新评注功能的开发过程

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: Tinymce 是一款非常流行的富文本编辑器,它提供了许多丰富的功能,包括格式刷功能。 使用 Tinymce 实现格式刷功能的方法如下: 第一步,首先需要配置 Tinymce 编辑器。具体方法是,需要在 Tinymce 的初始化函数中设置一个 toolbar 的数组,用来定义工具栏中显示的按钮。在这个数组中,需要添加一个 button,用来表示格式刷功能。 第二步,添加格式刷功能的代码。这一步可以使用 Tinymce 提供的 API 来实现格式刷功能。具体方法是,使用 getFormat 方法获取选中文本的格式,然后将这个格式应用到其它文本中。在应用格式的过程中,还可以使用 Tinymce 提供的 filter 方法来过滤一些非法的标签和属性。 第三步,添加一个按钮绑定格式刷功能。这个按钮可以添加到工具栏上,或者在页面上以其它形式显示。当用户点击这个按钮时,就会触发格式刷功能。 总之,Tinymce 可以通过一些简单的配置和代码实现格式刷功能。这个功能可以大大提高用户的编辑效率,使得文本编辑变得更加方便和易用。 ### 回答2: tinymce是一种基于JavaScript的富文本编辑器,可以在网页上直接编辑网页中的文字内容,实现所见即所得。在tinymce中,有许多实用的功能,其中包括格式刷功能。 格式刷功能是一种非常实用的文字排版工具,可以将已设定好格式的文字样式应用到其他文本上,从而快速实现一致的排版效果。在tinymce中,实现格式刷功能也很简单,具体步骤如下: 1. 打开tinymce编辑器,选择需要应用格式的文字。 2. 点击“格式刷”按钮,这个按钮通常是一个刷子的图标,可以在tinymce的工具栏中找到。 3. 然后,将鼠标的光标移到需要应用格式的文本上,进行单击。 4. 这时,文字的格式就会被应用到选中的文本中。 除了以上这种常规的格式刷功能,还可以在tinymce中自定义格式刷,具体步骤如下: 1. 打开tinymce编辑器,选择需要自定义的文本。 2. 点击“格式刷”按钮,在下拉菜单中选择“自定义格式刷”。 3. 然后,在弹出的对话框中,设置需要自定义的文字格式,比如字体、大小、颜色等。 4. 最后,点击“确认”按钮,将自定义的格式刷保存下来。 这样,在编辑文字时,就可以直接通过选择自定义的格式刷,快速应用之前设置好的文字格式,从而提高工作效率。总之,tinymce编辑器提供的格式刷功能与自定义格式刷功能,对于web前端工程师来说非常实用,帮助他们更加高效地进行网页排版设计,实现所见即所得的效果。 ### 回答3: tinymce是一个非常常用的富文本编辑器,它有着丰富的功能以及插件,其中也包括格式刷功能。格式刷功能可以使用户在编辑文本时更快速、更方便地将文本的格式进行统一。下面就是如何在tinymce实现格式刷功能的方法。 首先,需要在tinymce的配置项中添加一个按钮,该按钮即为格式刷按钮,用户点击该按钮可以启动格式刷的功能。为此,可以在tinymce配置项的toolbar选项中添加“formatselect”属性,用以显示格式刷按钮。例如: tinymce.init({ selector: 'textarea', toolbar: 'formatselect' }); 在添加了格式刷按钮之后,需要自定义格式刷的样式,以满足用户的需求。可以在tinymce的配置项中的formats选项中定义格式刷样式,例如: tinymce.init({ selector: 'textarea', toolbar: 'formatselect', formats: { custom: { block: 'p', attributes: { class: 'custom-style' } } } }); 上述代码中,我们定义了一个名为custom的自定义样式,该样式的标签是<p>,并且有一个class属性为custom-style。 最后,需要在点击格式刷按钮的时候,将选中的文本应用该格式刷样式。为此,可以在tinymce的配置项中的setup选项中添加一个函数来实现功能。例如: tinymce.init({ selector: 'textarea', toolbar: 'formatselect', formats: { custom: { block: 'p', attributes: { class: 'custom-style' } } }, setup: function (editor) { editor.addButton('formatbrush', { icon: 'formatbrush', tooltip: "Format Brush", onclick: function () { editor.formatter.apply('custom'); } }); } }); 上述代码中,我们添加了一个名为formatbrush的按钮,可以在tinymce中看到其图标为一个刷子。在点击该按钮的时候,调用editor.formatter.apply('custom')方法,即可应用我们定义的custom样式。 总之,通过以上步骤,便可在tinymce实现格式刷功能了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值