Photo credit: unsplash.com/photos/b18T…
preface
The purpose of this post is to document my first implementation of a very simple rich text editor that uses Webpack and TypeScript.
Project introduction
Project directory file
, | | - rich text editor -- README. Md, | -- index. HTML, | -- package - lock. Json, | -- package. Json, | -- tsconfig. Json, | -- webpack. Build. Config. Js, | -- webpack. Config. Js, | - dist, / / packaging generated file | | -- yangEditor. Js, | - lib, | | - index. The CSS, | |-- index.js, | |-- yangEditor.ts, |-- src, |-- index.js,Copy the code
Project installation dependencies
"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": "^ holdings"."webpack": "^ 5.30.0"."webpack-cli": "^ 4.6.0"."webpack-dev-server": "^ 3.11.2"
Copy the code
webpack.config.js
Development environmentWebpack
configuration
const path = require("path")
const HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
mode: "development".entry: "./src/index.js".devServer: {
port: 8089.// Set the port number
},
module: {
rules: [{test: /\.ts$/,
use: "ts-loader".exclude: /node_modules/
},
{
test: /\.css$/,
use: ["style-loader"."css-loader"]]}},plugins: [
new HtmlWebpackPlugin({
template: "./index.html"}})]Copy the code
webpack.build.config.js
Production environmentalWebpack
configuration
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"[}]}Copy the code
Project run command
"scripts": {
"dev": "webpack serve --open Chrome.exe"."build": "webpack --config webpack.build.config.js"
},
Copy the code
document.execCommand
Document. ExecCommand obsolete
When an HTML document is switched to design mode, document exposes the execCommand method, which allows commands to be run to manipulate elements in the editable content area, and is used to implement the rich text editor in this project.
grammar
bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)
, the return value is oneBoolean
If it isfalse
saidOperation not supported
orHas not been enabled
.
parameter
aCommandName
: a DOMString, the name of the command.aShowDefaultUI
: Whether to display the user interface. The value is usually false.aValueArgument
: Some commands (such as insertImage) require additional arguments (insertImage needs to provide the URL to insert the image), which defaults to null.
Simple rich text concrete implementation
yangEditor.ts
The file is in lib/ yangeditor.ts
First we create a class that passes in a DOM node string to get the DOM node.
export default class YangEditor {
constructor(dom: string) {
if(! dom) {throw new Error('Please pass in the DOM node!! ');
}
let editWrapper: HTMLElement = document.getElementById(dom); }}Copy the code
The Command instruction
Instructions to set the title, set the font color, set the font bold.
// The instruction array
const actions = [
{
command: 'formatblock'.values: [{text: '- Set the title size -'.value: 'selected' },
{ text: 'H1 title'.value: 'h1' },
{ text: 'H2 heading'.value: 'h2' },
{ text: 'H3 title'.value: 'h3' },
{ text: 'H4 title'.value: 'h4' },
{ text: 'the H5 title'.value: 'h5'},],}, {command: 'forecolor'.values: [{text: '- Set font color -'.value: 'selected' },
{ text: 'red'.value: 'red' },
{ text: 'blue'.value: 'blue' },
{ text: 'green'.value: 'green' },
{ text: 'black'.value: 'black'},],}, {command: 'bold'.values: [{text: '- Set font bold -'.value: 'selected' },
{ text: 'bold'.value: ' '},],},];Copy the code
createDOM
node
// Create a DOM nodeprivate createDOM(type: string, className? : string): HTMLElement {let dom = document.createElement(type);
dom.className = className || ' ';
return dom;
}
Copy the code
Dynamically createselect
The label
// Create a select node
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 the onchange event tag to call the execCommand method
this.execCommand(commandItem.command, select.options[select.selectedIndex].value);
};
return select;
}
Copy the code
usedocument.execCommand
methods
Invoke the onChange event on the select tag and call the execCommand method.
private execCommand(cmd: string, value: string): void {
//execCommand bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)
// aCommandName A DOMString, the name of the command.
//aShowDefaultUI a Boolean indicating whether to display the user interface, usually false
Some commands (such as insertImage) require additional arguments (insertImage requires the url to insert the image), which defaults to null.
document.execCommand(cmd, false, value);
}
Copy the code
Clicking outside the edit area still works
let range: Range = null; // To cache Range objects
// Move the mouse over the editing area
editContent.onmousemove = _.debounce(() = > {
let selection = window.getSelection();
if(! selection.isCollapsed) { range = selection.getRangeAt(0); // Return a reference to the specified Range that the selection contains}},100);
// Create a select node
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 tag onchange event
select.onchange = () = > {
let selection = window.getSelection();
selection.removeRange(selection.getRangeAt(0)); // Remove the Range object from the selection.
selection.addRange(range); // A Range object will be selected.
this.execCommand(commandItem.command, select.options[select.selectedIndex].value);
};
return select;
}
Copy the code
Method of use
import { YangEditor } from ".. /lib/index"
new YangEditor("yangEdit")
Copy the code
Implementation effect
Program source code
Editor source address
The resources
MDN execCommand