从零搭建一个简单的javascript富文本编辑器

560 阅读3分钟

前言: 最近接到消息,github开源项目github.com/wangeditor-… 在招募团队,作为一个多年前端开发小学生蠢蠢欲动,于是果断报名了。那么问题来了,如何从零开始搭建一个web富文本编辑器呢?阿蛮带你走进科学......

首先,开发一个富文本编辑器,需要了解富文本编辑器所需要的知识点。本文基于webpack+typescript开发,前期准备知识点如下:

  1. webpack www.webpackjs.com/concepts/
  2. typescript www.tslang.cn/
  3. sass sass.bootcss.com/documentati…
  4. document.execCommand developer.mozilla.org/zh-CN/docs/…
  5. selection developer.mozilla.org/en-US/docs/…

本文项目地址 github.com/lnimpossibl…

正文:

1.首先创建目录lnEditor

2.初始化package.json

npm init

3.安装依赖

npm install webpack webpack-cli webpack-dev-server
npm install ts-loader source-map-loader
npm install css-loader node-sass sass-loader

4.项目结构如下

dist--
--index.html (项目入口)
--bundle.js (打包后文件)
src--
--plugin (存放js插件,如颜色选择)
--styles (存放样式文件.scss)
--index.ts (脚本主入口)
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/36c8242a368848428054b5657d6e453a~tplv-k3u1fbpfcp-watermark.image)

5. webpack.config.js 配置如下

const path = require('path');
module.exports = {
entry: "./src/index.ts",
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
// Enable sourcemaps for debugging webpack's output.
devtool: "source-map",
resolve: {
// Add `.ts` and `.tsx` as a resolvable extension.
extensions: [".ts", ".tsx", ".js"]
},
//新建一个开发服务器,并且当代码更新的时候自动刷新浏览器。
devServer: {
contentBase: path.join(__dirname, "dist"),
compress: true,
port: 9000
}, // module.loaders 是最关键的一块配置。它告知 webpack每一种文件都需要使用什么加载器来处理:
module: {
rules: [
//解析.css文件
{
test: /\.scss$/,
use: [{
loader: "style-loader" // 将 JS 字符串生成为 style 节点
}, {
loader: "css-loader" // 将 CSS 转化成 CommonJS 模块
}, {
loader: "sass-loader" // 将 Sass 编译成 CSS
}]
}, { test: /\.js$/, loader: "source-map-loader" },
{ test: /\.tsx?$/, loader: "ts-loader" },
]
},
// 其它解决方案配置
resolve: { extensions: ['.ts', '.js', '.html', '.css', '.scss'], // 自动扩展文件后缀名,意味着我们引入模块可以不写后缀名
alias: { // 模块别名定义
"@": path.join(__dirname, "src")
},
}
// Other options...
};

6. 核心代码

1.封装document.execComman如下:

const exec = (command: string, value: any = null) => {
  document.execCommand(command, false, value);
};
  1. 富文本编辑器顶部button数据
const actions = {
  bold: {
    icon: 'B',
    title: 'Bold',
    result: () => exec('bold')
  },
  italic: {
    icon: 'I',
    title: 'Italic',
    result: () => exec('italic')
  },
  underline: {
    icon: 'U',
    title: 'Underline',
    result: () => exec('underline')
  },
  link: {
    icon: '🔗',
    title: 'Link',
    result: () => {
      const url = window.prompt('Enter the link URL')
      if (url) exec('createLink', url)
    }
  },
  color: {
    icon: '',
    title: 'color',
    result: () => {
      // exec('foreColor', 'red') (由于颜色选取不是简单的点击事件,需要在色板change的时候操作exec)
    }
  }
  // … others button
}
for (let k in actions) {
  const v: any = actions[k];
  // 新建一个按钮元素
  const button = document.createElement('button')
  // 给按钮加上 css 样式
  button.className = 'item'
  // 把 icon 属性作为内容显示出来
  button.innerHTML = v.icon
  button.title = v.title
  // 把 result 属性赋给按钮作为点击事件
  button.onclick = v.result
  // 将创建的按钮添加到工具栏上
  actionbar.appendChild(button)
}

7. 颜色选择器 jsColor

github.com/EastDesire/…

const myPicker: void = new wid.jscolor('#colorButton', {
  onChange: function () {
    const rgba: string = this.toRGBAString()
    // 改变颜色的时候,执行exec更改文字颜色
    exec('foreColor', rgba)
  }
})

8. 补充:实现点击外围空白处,再点击顶部按钮,仍能对上次选中的文字进行编辑

思路如下:

  1. 在执行execCommand之前先判断selection是否有选中range,如果没有则使用上次的range
  2. 在编辑器增加onmouseleave事件,每次离开的时候,只要有选中的range 就保存 lastRange
  3. selection对象isCollapsed用了判断是否框选,开始偏移量与结束偏移量不相同,所以false是为已经框选 具体代码
let lastRange: any = null
document.getElementById("inputBody").onmouseleave = function() {
  var selObj = window.getSelection(); // Selection对象
  // 判断是否框选了, 开始偏移量与结束偏移量不相同,所以为false.
  if(selObj.isCollapsed === false) {
    var range  = selObj.getRangeAt(0); //  Range 对象
    // 保存上次的框选range
    lastRange = range
  }
}
const exec = (command: string, value: string | null = null) => {
  var selObj = window.getSelection(); // Selection对象
  if(selObj.isCollapsed === true) {
    selObj.removeAllRanges()
    // 使用上次的框选区域range
    selObj.addRange(lastRange)
  }
  document.execCommand(command, false, value);
};

一个简单的富文本编辑器到此就完成了。本文项目地址 github.com/lnimpossibl…