道招

富文本编辑器wangEditor多语言、工具栏体验改造

如果您发现本文排版有问题,可以先点击下面的链接切换至老版进行查看!!!

富文本编辑器wangEditor多语言、工具栏体验改造

支持多语言

首页wangEditro本身是支持使用多语言的,通过源码可以看到里面有个replaceLang方法,虽然这种写法有点非主流,但是是有效的,里面部分地方在支持多语言方面有些遗漏。

以“加粗”功能为例

// 构造函数
function Bold(editor) {
  this.editor = editor;
  this.title = '加粗';
  const title = replaceLang(editor, this.title);
  this.$elem = $(
    '<div class="w-e-menu">\n            <i class="w-e-icon-bold" title="' + title + '"></i>\n        </div>',
  );
  this.type = 'click';

  // 当前是否 active 状态
  this._active = false;
}

看下replaceLang方法干了什么

/*
    替换多语言
 */

var replaceLang = function(editor, str) {
  var langArgs = editor.config.langArgs || [];
  var result = str;

  langArgs.forEach(function(item) {
    var reg = item.reg;
    var val = item.val;

    if (reg.test(result)) {
      result = result.replace(reg, function() {
        return val;
      });
    }
  });

  return result;
};

通过传入参数langArgs数组,然后进行遍历,这个方法简单粗暴,缺点也很明显,每次调用replaceLang就会把整个数组遍历一遍。。。 wangEditor的源码里面是直接写的中文,对于加粗的话,你可能需要这样才行:

[{
 reg: '加粗',
 value: 'Bold',
}]

在现在很多公司的发布工具会扫描中文的,那样源码的里面的“加粗”肯定是中招了。

工具栏体验改造

file

wangEditor默认是支持加粗后对应的图标B变成蓝色高亮的,类似的还有斜体、下划线、删除线,但是字体颜色是没有类似的功能(可能是作者偷懒了吧),这样会导致体验不佳。 上图中最后的暗红色铅笔图标就是显示的字体颜色,是我后期自行实现,我们现在看看具体怎么实现的吧。

依葫芦画瓢,我们看下“加粗”的这个功能怎么实现的

// 原型
Bold.prototype = {
  constructor: Bold,

  // 点击事件
  onClick: function onClick(e) {
    // 点击菜单将触发这里

    var editor = this.editor;
    var isSeleEmpty = editor.selection.isSelectionEmpty();

    if (isSeleEmpty) {
      // 选区是空的,插入并选中一个“空白”
      editor.selection.createEmptyRange();
    }

    // 执行 bold 命令
    editor.cmd.do('bold');

    if (isSeleEmpty) {
      // 需要将选取折叠起来
      editor.selection.collapseRange();
      editor.selection.restoreSelection();
    }
  },

  // 试图改变 active 状态
  tryChangeActive: function tryChangeActive(e) {
    var editor = this.editor;
    var $elem = this.$elem;
    if (editor.cmd.queryCommandState('bold')) {
      this._active = true;
      $elem.addClass('w-e-active');
    } else {
      this._active = false;
      $elem.removeClass('w-e-active');
    }
  },
};

我们可以看到里面有queryCommandState('bold'),对就是document.queryCommandState这个API,我们可以查询到当前光标处是否实行了加粗,我们可以在MDN上看看它支持哪些参数。

tryChangeActive是什么时候调用的呢?

// 尝试修改菜单状态
  changeActive: function changeActive() {
    var menus = this.menus;
    objForEach(menus, function(key, menu) {
      if (menu.tryChangeActive) {
        setTimeout(function() {
          menu.tryChangeActive();
        }, 100);
      }
    });
  },

调用changeActive是会调用菜单每一项的tryChangeActive 方法。

那我们是什么时候调用changeActive呢?

有两个地方

1.保存选区
// 实时保存选取
  _saveRangeRealTime: function _saveRangeRealTime() {
    var editor = this.editor;
    var $textElem = editor.$textElem;

    // 保存当前的选区
    function saveRange(e) {
      // 随时保存选区
      editor.selection.saveRange();
      // 更新按钮 ative 状态
      editor.menus.changeActive();
    }
    // 按键后保存
    $textElem.on('keyup', saveRange);
    $textElem.on('mousedown', function(e) {
      // mousedown 状态下,鼠标滑动到编辑区域外面,也需要保存选区
      $textElem.on('mouseleave', saveRange);
    });
    $textElem.on('mouseup', function(e) {
      saveRange();
      // 在编辑器区域之内完成点击,取消鼠标滑动到编辑区外面的事件
      $textElem.off('mouseleave', saveRange);
    });
  },
2.调用do方法
// 修改原型
Command.prototype = {
  constructor: Command,

  // 执行命令
  do: function _do(name, value) {
    var editor = this.editor;

    // 使用 styleWithCSS
    if (!editor._useStyleWithCSS) {
      document.execCommand('styleWithCSS', null, true);
      editor._useStyleWithCSS = true;
    }

    // 如果无选区,忽略
    if (!editor.selection.getRange()) {
      return;
    }

    // 恢复选取
    editor.selection.restoreSelection();

    // 执行
    var _name = '_' + name;
    if (this[_name]) {
      // 有自定义事件
      this[_name](value);
    } else {
      // 默认 command
      this._execCommand(name, value);
    }

    // 修改菜单状态
    editor.menus.changeActive();

    // 最后,恢复选取保证光标在原来的位置闪烁
    editor.selection.saveRange();
    editor.selection.restoreSelection();

    // 触发 onchange
    editor.change && editor.change();
  },
  ...
}

我们改造实现下前面提到的字体颜色。

function ForeColor(editor) {
  this.editor = editor;
  this._action = 'foreColor';
  ...
}

ForeColor.prototype = {
    constructor: ForeColor,
    ...
    tryChangeActive: function tryChangeActive(e) {
        checkIconActive.call(this, 'ForeColor');
    }
    ...
}
function checkIconActive(type) {
  var editor = this.editor;
  var $elem = this.$elem;
  let color = editor.cmd.queryCommandValue(this._action);
  color = colorRGBtoHex(color);
  color = color === '#ffffff' ? '#eeece0' : color; // 纯白色不好识别,改用#eeece0
  $elem.children()[0].style.color = color;
}

看了上面的写法,有经验的朋友应该会想到背景色也可以这样实现,修改this._action的值即可。

这样我们就实现此类工具栏的改造,我们可以按照类似的方法实现另外两类工具栏图标的改造。 一类是设置标题这种,只有有对应“样式”就蓝色高亮,在hover时下拉列表显示匹配到的样式。 file 另一类就是对齐方式、列表方式这种,相比上一种多一个同步修改工具栏对应icon的功能。 file

这两类就直接上代码了。

function checkActive(type) {
  const $elem = this.$elem;
  const editor = this.editor;
  const values = this._actions.map(item => editor.cmd.queryCommandState(item) && item);
  const [activeValue] = values.filter(item => item);
  const deletedValues = this._actions.filter(item => item !== activeValue);
  const elemClassList = $elem.children()[0].classList;
  deletedValues.forEach(item => {
    const inActiveClassName = this._iconMap[item];
    elemClassList.remove(inActiveClassName);
    $elem.find('.' + inActiveClassName).removeClass('w-e-active');
  });
  const activeClassName = this._iconMap[activeValue];
  if (activeClassName) {
    this._active = true;
    elemClassList.add('w-e-active');
    elemClassList.add(activeClassName);
    $elem.find('.' + activeClassName).addClass('w-e-active');
  } else {
    this._active = false;
    const defaultClassName = this._iconMap.default;
    elemClassList.remove('w-e-active');
    elemClassList.add(defaultClassName);
  }
}

function checkListActive(type, getValue) {
  const $elem = this.$elem;
  const editor = this.editor;
  const value = typeof getValue === 'function' ? getValue() : editor.cmd.queryCommandValue(this._action);
  const children = $elem.find('ul.w-e-list').children();
  if (!children) {
    return;
  }
  children.removeClass('w-e-active');
  // 先清除
  const elemClassList = $elem.children()[0].classList;
  elemClassList.remove('w-e-active');
  const activeIndex = this._iconMap[value];
  if (activeIndex !== void 0) { // 能枚举到的就设为active
    // 后设置
    this._active = true;
    elemClassList.add('w-e-active');
    children[activeIndex] && children[activeIndex].classList.add('w-e-active');
  } else {
    this._active = false;
    elemClassList.remove('w-e-active');
  }
}
更新时间:
上一篇:命令式组件Message、Dialog的主流写法分析下一篇:WordPress博客项目改用react前端展示

相关文章

在iframe中使用富文本编辑器wangEditor

自己做的邮件项目里面需要使用到富文本编辑器,邮件内容说白了就是HTML代码。前任使用的是wangEditor,部分定制化需求就是直接改的源码。 最近发现有的用户的邮件内容加进去的很多css信息, 阅读更多…

关注道招网公众帐号