Use the browser native interface to achieve a simple rich text editor.

introduce

Rich text editors are used in many scenarios, so what is the implementation principle? This paper uses 100 lines of code to implement a simple rich text editor, and introduces the principle.

Experience the link

Simple rich text editor Demo

Here’s what a rich text editor looks like:

Community mature product

  • braft-editor

  • react-quill

Introduction to implementation Principles

1. contentEditable

The contentEditable property of HTML lets you edit the tag content on any web page.

Below, you can edit headlines, text, links, and even images.

<div contentEditable="true">
    <h2>This is a title,</h2>
    <p>This is a text</p>
    <a href="http://baidu.com">This is a link</a>
</div>
Copy the code

PS: To prohibit to copy the text or need to log in to copy the text of the web page, with this trick can be perfect crack:

  • Open the console and rundocument.body.contentEditable = truePage can copy any area text.

For details about contentEditable, see MDN – contentEditable

2. document.execCommand

This is a browser API that exposes a execCommand method to manipulate the current editable area when the element is in edit mode.

  • ExecCommand execCommand execCommand execCommand execCommand execCommand execCommand

] (developer.mozilla.org/zh-CN/docs/…).

For example, in a nutshell

  • Open a web page with text, first make the page editable, console rundocument.body.contentEditable = true
  • Then select some text page, console rundocument.execCommand('bold', false), can make the selected area bold

See MDN – document.execCommand for more commands

Here are some of the commands:

The demo source code

Below is the complete source code of the demo above, which implements setting font, size, bold, tilt, alignment, and so on.

The main principle is to analyze the style of the selected text, and then determine which icon of the toolbar is hit.

Feel good welcome to the article like collection oh


<! DOCTYPEhtml>
<html>
<head>
    <title>Simple rich text editor</title>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <link rel="shortcut icon" href="./icons/favicon.ico">
    <link rel="stylesheet" href="./article.css">
    <style>
        .container {
            max-width: 1000px;
            margin: 0 auto;
            position: relative;
        }
        .commandZone {
            margin: 20px 0;
            border: 1px solid #aaa;
            padding: 10px;
            padding-bottom: 0;
            display: flex;
            align-items: center;
            flex-wrap: wrap;
        }
        .editor-container {
            border: 1px solid #ccc;
            padding: 15px;
            outline: none;
            line-height: 1.8;
            max-height: 500px;
            overflow-y: auto;
        }
        .editor-container:focus {
            border: 1px solid # 999;
        }
        .btn {
            color: black;
            padding: 0 5px;
            display: inline;
            cursor: pointer;
            font-size: 12px;
            height: 21px;
        }
        .icon {
            cursor: pointer;
            color: # 999;
        }
        .icon:hover {
            color: # 000;
        }
        .tool {
            margin-right: 10px;
            margin-bottom: 10px;
        }
        select {
            width: 60px;
            height: 21px;
        }
        .editor-editable {
            position: absolute;
            right: 0;
            top: 5px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1 style="text-align: center; margin: 5px 0;">Simple rich text editor</h1>
        <button class="editor-editable btn" id="editable">Only show</button>
        <div class="commandZone" id="commandZone"></div>
        <div class="editor-container" id="editor" contenteditable="true"></div>
        <div class="editor-container" contenteditable="true" style="margin-top: 20px;">This is another rich text box, just set it<code>contenteditable="true"</code>You can edit</div>
    </div>
</body>
<script>
    function execEditorCommand(name, args = null) {
        document.execCommand(name, false, args);
    }

    const selectRender = (commandName, options = [], title = ' ') = > {
        return `
            <select class="tool" name="${commandName}" id="${commandName}" title="${title}">
                ${options.map((e) => `<option class="${commandName}-option" value="${e.value}">${e.label}</option>`)}
            </select>
        `;
    };

    const addEventListener = (commandName) = > {
        const eventNameMap = {
            fontName: 'change'.fontSize: 'change'.backColor: 'change'.foreColor: 'change'.styleWithCSS: 'change'.contentReadOnly: 'change'.heading: 'change'};const needInputUrl = ['insertImage'.'createLink'];
        const eventName = eventNameMap[commandName] || 'click';
        const dom = document.getElementById(commandName);
        dom && dom.addEventListener(eventName, () = > {
            if (eventName === 'click') {
                if (needInputUrl.includes(commandName)) {
                    const value = window.prompt('Please enter a link');
                    execEditorCommand(commandName, value);
                } else{ execEditorCommand(commandName); }}else if (eventName === 'change') {
                const className = `${commandName}-option`;
                const optionSelectedIndex = document.getElementsByClassName(className);
                const value = optionSelectedIndex[dom.selectedIndex].value;
                // debuggerexecEditorCommand(commandName, value); }}); };const defaultHtml = '

Vue. Js

< I >Vue is a set of < strong > progressive framework < / strong > < / font > < / I >. Vue's core library focuses only on the view layer, making it easy to get started and integrate with third-party libraries or existing projects. On the other hand , When the < a href = "https://vuejs.bootcss.com/guide/single-file-components.html" > modern tool chain < / a > and < a Href = "https://github.com/vuejs/awesome-vue#libraries - plugins" target = "_blank" rel = "noopener" > support library < / a > when used in combination, Vue is also perfectly capable of providing drivers for complex, single-page applications. < / p > < p > if you want to know more about it before further learning Vue, we < a id = "modal - player" href = "https://vuejs.bootcss.com/guide/#" > made a video < / a >, Takes you through its < U > core concept and a < U > sample project . < / p > < p > if you are already experienced front-end developer, wanted to know what difference between Vue and other library/framework, please see < a href = "https://vuejs.bootcss.com/guide/comparison.html" > < / a > compared to other framework.


What is Vue.js?

Vue is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable. The core library is focused on the view layer only, and is easy to pick up and integrate with other libraries or existing projects. On the other hand, Vue is also perfectly capable of powering sophisticated Single-Page Applications when used in combination with modern tooling and supporting Libraries .

If you'd like to learn more about Vue before diving in, we created a video walking through the core principles and a sample project.

If you are an experienced frontend developer and want to know how Vue compares to other libraries/frameworks, check out the Comparison with Other Frameworks.


< p > < b > unordered list < / b > < / p > < ul > < li > list items < / li > < li > list items < / li > < li > list items < / li > < / ul > < p > < b > ordered list < / b > < / p > < ol > < li > list items < / li > < li > list items < / li > < li > < list item/li > < / ol > < p > < b > image < / b > < / p > < p > < img SRC = "https://alanyf.gitee.io/personal-website/assets/icon/wangyiyun.svg" > < / p > < hr >

y = x2 + 4

NaOH    CH4

  [Ar]3d104s24p6


< p style="text-align: center; > Right align

'
; const commandMap = { undo: { name: 'undo'.command: 'undo',},redo: { name: 'redo'.command: 'redo',},fontName: { name: 'Font name'.command: 'fontName'.render: () = > { const options = [ { label: Microsoft Yahei.value: 'Microsoft YaHei' }, { label: New Rome.value: 'Times New Roman' }, { label: '宋体'.value: 'SimSun' }, { label: 'square'.value: 'PingFang SC' }, { label: 'Chinese Script'.value: 'STKaiti' }, { label: 'Arial'.value: 'Arial' }, { label: 'Calibri'.value: 'Calibri' }, { label: 'Comic Sans MS'.value: 'Comic Sans MS' }, { label: 'Verdana'.value: 'Verdana'},];return selectRender('fontName', options, 'Font name'); }},fontSize: { name: 'Font size'.command: 'fontSize'.render: () = > { const options = [ { label: 'special small'.value: '1' }, { label: 'small'.value: '2' }, { label: 'normal'.value: '3' }, { label: 'a little big'.value: '4' }, { label: 'big'.value: '5' }, { label: 'a'.value: '6' }, { label: 'great'.value: '7'},];return selectRender('fontSize', options, 'Font size'); }},heading: { name: 'title'.command: 'heading'.render: () = > { const options = [ { label: 'H1'.value: 'H1' }, { label: 'H2'.value: 'H2' }, { label: 'H3'.value: 'H3' }, { label: 'H4'.value: 'H4' }, { label: 'H5'.value: 'H5' }, { label: 'H6'.value: 'H6'},];return selectRender('heading', options, 'title'); }},bold: { name: 'bold'.command: 'bold',},italic: { name: 'italics'.command: 'italic',},underline: { name: 'underline'.command: 'underline',},strikeThrough: { name: 'Delete line'.command: 'strikeThrough',},backColor: { name: 'Background color'.command: 'backColor'.render: () = > { const options = [ { label: 'black'.value: 'black' }, { label: 'red'.value: 'red' }, { label: 'orange'.value: 'orange' }, { label: 'blue'.value: 'blue' }, { label: 'green'.value: 'green' }, { label: 'white'.value: 'white' }, { label: 'grey'.value: '# 999' }, { label: 'light grey'.value: '#ddd'},];return selectRender('backColor', options, 'Background color'); }},foreColor: { name: 'Font color'.command: 'foreColor'.render: () = > { const options = [ { label: 'black'.value: 'black' }, { label: 'red'.value: 'red' }, { label: 'orange'.value: 'orange' }, { label: 'blue'.value: 'blue' }, { label: 'green'.value: 'green' }, { label: 'white'.value: 'white' }, { label: 'grey'.value: '# 999' }, { label: 'light grey'.value: '#ddd'},];return selectRender('foreColor', options, 'Font color'); }},superscript: { name: 'superscript'.command: 'superscript',},subscript: { name: 'the subscript'.command: 'subscript',},justifyCenter: { name: 'Center aligned'.command: 'justifyCenter',},justifyFull: { name: 'Align both ends'.command: 'justifyFull',},justifyLeft: { name: 'Left aligned'.command: 'justifyLeft',},justifyRight: { name: 'Right aligned'.command: 'justifyRight',},removeFormat: { name: 'Clear style'.command: 'removeFormat',},insertHorizontalRule: { name: 'Dividing line'.command: 'insertHorizontalRule',},insertUnorderedList: { name: 'Unordered list'.command: 'insertUnorderedList',},insertOrderedList: { name: 'Ordered list'.command: 'insertOrderedList',},increaseFontSize: { name: 'Font size up'.command: 'increaseFontSize',},decreaseFontSize: { name: 'Font size smaller'.command: 'decreaseFontSize',},styleWithCSS: { name: 'Marking mode'.command: 'styleWithCSS'.render: () = > { const options = [ { label: 'HTML tags'.value: false }, { label: 'CSS'.value: true},];return selectRender('styleWithCSS', options, 'Markup (using HTML tags or CSS styles to generate markup)'); }},createLink: { name: 'Insert link'.command: 'createLink',},insertImage: { name: 'Insert picture'.command: 'insertImage',},contentReadOnly: { name: 'Is the region editable?'.command: 'contentReadOnly'.render: () = > { const options = [ { label: 'is'.value: true }, { label: 'no'.value: false},];return selectRender('contentReadOnly', options, 'Is the region editable?'); ,}}};const commands = [ 'undo'.'redo'.'fontName'.'fontSize'.// 'heading', 'bold'.'italic'.'underline'.'strikeThrough'.'backColor'.'foreColor'.'superscript'.'subscript'.// Align the cursor insertion position or the selected text 'justifyCenter'.'justifyFull'.'justifyLeft'.'justifyRight'.// Remove all formatting for the selected content 'removeFormat'.'insertHorizontalRule'.'insertUnorderedList'.'insertOrderedList'.// 'increaseFontSize', // 'decreaseFontSize', 'createLink'.'insertImage'.// Tags use HTML tags or CSS 'styleWithCSS'.// 'contentReadOnly', ]; const commandZone = document.getElementById('commandZone'); const editor = document.getElementById('editor'); const editable = document.getElementById('editable'); const htmlList = commands.map((commandName) = > { const command = commandMap[commandName]; if(! command) {return ' '; } if (command.render) { return command.render(); } // <button class="btn tool" id="${commandName}">${command.name}</button> return ` <img class="icon tool" id="${commandName}" title="${command.name}" src="./icons/${commandName}.svg"/> `; }); commandZone.innerHTML = htmlList.join('\n'); editor.innerHTML = defaultHtml; editable.addEventListener('click'.(e) = > { const html = e.target.innerHTML.trim(); consteditable = html ! = ='edit'; e.target.innerHTML = editable ? 'edit' : 'Show only'; editor.setAttribute('contentEditable', !editable); }); editor.addEventListener('mouseup'.() = > { console.log(document.getSelection()); }); setTimeout(() = > { commands.forEach((commandName) = > addEventListener(commandName)); }, 100);
</script> </html> Copy the code