This article will show you how to learn to read a good open source project called wangEditor

WangEditor version 4

Typescript Web rich text editor, lightweight, concise, easy to use, open source free Github portal

1. Find the entry file

src/wangEditor.ts

It can be seen that the entry file is very concise, and you can clearly understand some of the files required by wangEidtor

  • 1. Style file less
  • 2. Wangeditor class filesimport Editor from './editor/index'
  • 3. Polyfill some functionally compatible filesimport './utils/polyfill'
  • 4. Initialize some basic menu classes that can be extended and configuredexport * from './menus/menu-constructors/index'

Note: do not parse less, the usage of less can refer to the less official website documentation

2. Open the

src/utils/polyfill

2. The following two functions are compatible, and the comments are also written clearly:

    1. A compatible implementation of the matches method
    1. Promise compatibility for IE11

3. The key

src/editor/index.ts

This file is the core entry to the editor

The first is the constructor

This constructor takes two arguments

  • ToolbarSelector – must – Selector in the editor toolbar
  • TextSelector — Optional — A selector for the editor’s content

This function initializes the function and attribute configuration required by the editor, as shown in figure

The rest are apis exposed to the user
API note
create() Create an editor instance
beforeDestroy() Destroy front hook
destroy() Destroy editor
fullScreen() Enter the full screen
unFullScreen() Shut down full screen
scrollToHead() Jumps to the specified anchor point
registerMenu() Register extension Menu

4. Select API encapsulation

src/editor/selection.ts

API note
getRange() Get the current range
saveRange() Save the selection range
saveRange() Fold selection range
getSelectionText() Gets the text in the selection range
getSelectionContainerElem() Gets the DOM element of the selection range
getSelectionStartElem() The DOM element at the beginning of the selection range
getSelectionEndElem() The DOM element at the end of the selection range
isSelectionEmpty() Is the selection empty (no text is selected)
restoreSelection() Restore selection range
createEmptyRange() Create a blank (that is, &#8203 characters) selection
createRangeByElems() Reset selection
createRangeByElem() Set the selection based on the DOM element
getSelectionRangeTopNodes() Gets the top-level (paragraph) element of the current selection range
moveCursor() Moves the cursor position, by default at the tail
getCursorPos() Gets the cursor position in the current selection
clearWindowSelectionRange() Clears the Range in the current selection. Notice: Does not affect the saved Range
recordSelectionNodes() Record node – from the start node of the selection to the end node of the selection

Summary: This file encapsulates selection and cursor operations for Selection

5. ExecCommand Encapsulates the command

src/editor/command.ts

API note
do() A command that performs a rich text operation
insertHTML() Insert the HTML
insertElem() Insert DOM elements

Summary: Encapsulates the above three, and there are a few more that are simply added a layer of native encapsulation, which will not be listed. These are some encapsulation for execCommand

6. Initialize the edit area

src/text/index.ts

API note
togglePlaceholder() Switch the placeholder
clear() Empty content
html() Get content/set content
text() Get text/Set text
setJson() Set the json
getJson() Get a json
append() Append HTML content

In addition, some apis are private, so I won’t introduce them. If you are interested, you can check their functions on your own

Summary: Exposed some apis for editing area operations for users to use

There is also a feature worth mentioning here, which is the global management of the event delegate, which takes part of the code:

// keyboard down
$textElem.on('keydown'.(e: KeyboardEvent) = > {
    const keydownEvents = eventHooks.keydownEvents
    keydownEvents.forEach(fn= > fn(e))
})
Copy the code

The keyDown events for the edit area are placed in an array of keydownEvents, and the loop is executed to reach global management, very elegant.

7. Initialize the menu

src/menus/index.ts

API note
extend Custom add menu
init Initialization menu
menuFind Gets the specified menu object
changeActive Modify menu activation status

This is a class file for menu management and initialization

8. Global zIndex management

src/editor/z-index/index.ts

This file is a unified management of all the styles used by the editor, zIndex

9. Listen for the implementation of changes in editor content

src/editor/change/index.ts

It inherited the Mutation, we follow the open the packaging of Mutation file SRC/utils/observer/Mutation. The ts

Soga, originally using the native MutationObserver API

The MutationObserver API creates and returns a new MutationObserver, which is called when the specified DOM changes

The example used by the Official Web API

// Select the nodes that need to be observed to change
const targetNode = document.getElementById('some-id');

// Observer configuration (what changes need to be observed)
const config = { attributes: true.childList: true.subtree: true };

// The callback function that is executed when changes are observed
const callback = function(mutationsList, observer) {
    // Use traditional 'for loops' for IE 11
    for(let mutation of mutationsList) {
        if (mutation.type === 'childList') {
            console.log('A child node has been added or removed.');
        }
        else if (mutation.type === 'attributes') {
            console.log('The ' + mutation.attributeName + ' attribute was modified.'); }}};// Create an observer instance and pass in the callback function
const observer = new MutationObserver(callback);

// Observe the target node from the above configuration
observer.observe(targetNode, config);

// After that, you can stop observing
observer.disconnect();
Copy the code

It is an event API that listens for changes in the STRUCTURE of the DOM tree.

10. Undo and restore functions

src/editor/history/index.ts

To see the implementation, construct the code:

We start with three caches

    1. Content caching
    1. Scroll scrollTop cache
    1. District cache

SRC /utils/data-structure/cache.ts SRC /utils/data-structure/cache.ts

Several protected properties are set

    1. Data: Caches generated by normal operations (user input, JS code modification, recovery)
    1. RevokeData: Cache generated by an undo operation (needed for a recovery operation)
    1. IsRe: Whether the previous step was undo/restore

The constructor passes a maximum size to limit the maximum cache storage

 constructor(protected maxSize: number) {
        this.data = new CeilStack(maxSize)
        this.revokeData = new CeilStack(maxSize)
 }
Copy the code

CeilStack is a data structure that stores the cache. It uses an array to cache the stack and achieves the last in, first out effect

To be continued