使用 Webpack + TypeScript 实现简易的富文本编辑器

吐槽君 分类:javascript

artem-sapegin-DErxVSSQNdM-unsplash.jpg

图片来源 unsplash.com/photos/b18T…

前言

写这篇文章的目的是为了记录自己第一次实现一个非常简易的富文本编辑器的过程,项目使用到了 Webpack 和 TypeScript。

项目介绍

项目目录文件

  |-- 富文本编辑器,
      |-- README.md,
      |-- index.html,
      |-- package-lock.json,
      |-- package.json,
      |-- tsconfig.json,
      |-- webpack.build.config.js,
      |-- webpack.config.js,
      |-- dist,// 打包生成的文件
      |   |-- yangEditor.js,
      |-- lib,
      |   |-- index.css,
      |   |-- index.js,
      |   |-- yangEditor.ts,
      |-- src,
          |-- index.js,
  
 

项目安装的依赖

"clean-webpack-plugin": "^3.0.0",
"css-loader": "^5.2.0",
"html-webpack-plugin": "^5.3.1",
"style-loader": "^2.0.0",
"ts-loader": "^8.1.0",
"typescript": "^4.2.3",
"webpack": "^5.30.0",
"webpack-cli": "^4.6.0",
"webpack-dev-server": "^3.11.2"
 

webpack.config.js 开发环境的 Webpack 配置

const path = require("path")
const HtmlWebpackPlugin = require("html-webpack-plugin")

module.exports = {
    mode: "development",
    entry: "./src/index.js",
    devServer: {
        port: 8089, // 设置端口号
    },

    module: {
        rules: [
            {
                test: /\.ts$/,
                use: "ts-loader",
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            }
        ]
    },

    plugins: [
        new HtmlWebpackPlugin({
            template: "./index.html"
        })
    ]
}
 

webpack.build.config.js 生产环境的 Webpack 配置

const path = require("path")

module.exports = {
    mode: "production",
    entry: "./lib/index.js",
    output: {
        filename: "yangEditor.js",
        path: path.resolve(__dirname, "./dist"),
        libraryTarget: 'umd'
    },
    
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: "ts-loader",
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            }
        ]
    }
}
 

项目运行命令

"scripts": {
		"dev": "webpack serve --open Chrome.exe",
		"build": "webpack --config webpack.build.config.js"
	},
 

document.execCommand

document.execCommand 已废弃

当一个HTML文档切换到设计模式时,document暴露 execCommand 方法,该方法允许运行命令来操纵可编辑内容区域的元素,本次项目实现富文本编辑器就是使用该它。

语法

  • bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument),返回值是一个 Boolean ,如果是 false 则表示操作不被支持未被启用

参数

  • aCommandName:一个 DOMString ,命令的名称。
  • aShowDefaultUI:是否展示用户界面,一般为 false。
  • aValueArgument:一些命令(例如insertImage)需要额外的参数(insertImage需要提供插入image的url),默认为null。

简易富文本具体实现

yangEditor.ts

该文件在lib/yangEditor.ts

首先我们创建一个类,传入一个DOM节点字符串,获取DOM节点。

export default class YangEditor {
	constructor(dom: string) {
		if (!dom) {
			throw new Error('请传入DOM节点!!!');
		}
		let editWrapper: HTMLElement = document.getElementById(dom);
	}
}
 

Command 指令

指令有设置标题,设置字体颜色,设置字体加粗。

// 指令数组
const actions = [
	{
		command: 'formatblock',
		values: [
			{ text: '- 设置标题大小 -', value: 'selected' },
			{ text: 'H1标题', value: 'h1' },
			{ text: 'H2标题', value: 'h2' },
			{ text: 'H3标题', value: 'h3' },
			{ text: 'H4标题', value: 'h4' },
			{ text: 'H5标题', value: 'h5' },
		],
	},
	{
		command: 'forecolor',
		values: [
			{ text: '- 设置字体颜色 -', value: 'selected' },
			{ text: '红色', value: 'red' },
			{ text: '蓝色', value: 'blue' },
			{ text: '绿色', value: 'green' },
			{ text: '黑色', value: 'black' },
		],
	},
	{
		command: 'bold',
		values: [
			{ text: '- 设置字体粗体 -', value: 'selected' },
			{ text: '粗体', value: '' },
		],
	},
];
 

创建DOM节点

// 创建DOM节点
	private createDOM(type: string, className?: string): HTMLElement {
		let dom = document.createElement(type);
		dom.className = className || '';
		return dom;
	}
 

动态创建 select 标签

// 创建select节点
	private createSelectDOM(commandItem: ActionsItem): HTMLSelectElement {
		let select = document.createElement('select');
		commandItem.values.forEach((item) => {
			select.add(new Option(item.text, item.value));
			select.id = `${commandItem.command}`;
		});
		
		select.onchange = () => {
                       // select标签onchange事件,调用execCommand方法
			this.execCommand(commandItem.command, select.options[select.selectedIndex].value);
		};
		return select;
	}
 

使用 document.execCommand 方法

在select标签调佣onchange事件,调用execCommand方法。

private execCommand(cmd: string, value: string): void {
		//execCommand bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)
		// aCommandName 一个 DOMString ,命令的名称。
		//aShowDefaultUI 一个 Boolean, 是否展示用户界面,一般为 false
		//aValueArgument 一些命令(例如insertImage)需要额外的参数(insertImage需要提供插入image的url),默认为null。
		document.execCommand(cmd, false, value);
	}
 

点击编辑区域以外后点击指令依然有效

     let range: Range = null; // 用于缓存Range对象

     // 鼠标在编辑区域移动
     editContent.onmousemove = _.debounce(() => {
        let selection = window.getSelection();
        if (!selection.isCollapsed) {
        range = selection.getRangeAt(0); // 返回选区包含的指定区域(Range)的引用
	   }
	}, 100);
        
        
        // 创建select节点
	private createSelectDOM(commandItem: ActionsItem): HTMLSelectElement {
		let select = document.createElement('select');
		commandItem.values.forEach((item) => {
			select.add(new Option(item.text, item.value));
			select.id = `${commandItem.command}`;
		});

		// select标签onchange事件
		select.onchange = () => {
			let selection = window.getSelection();

			selection.removeRange(selection.getRangeAt(0)); // 将Range对象将从选区当中移除。

			selection.addRange(range); // 一个区域(Range)对象将被加入选区。

			this.execCommand(commandItem.command, select.options[select.selectedIndex].value);
		};
		return select;
	}
 

使用方法

import { YangEditor } from "../lib/index"
new YangEditor("yangEdit")
 

实现效果

屏幕录制2021-04-05 上午11.gif

项目源码

编辑器源码地址

参考资料

MDN execCommand

回复

我来回复
  • 暂无回复内容