DOMstructure

First of all, the renderings:

First of all, some of the details or techniques of this article are based on another article of mine. If you are not clear about something while reading, you can go to that article first and maybe find the answer.

The markDown input box is on the left and the corresponding MarkDown output instant preview box is on the right. The two element boxes can scroll with each other.

It can be basically determined from the renderings that the whole page is roughly divided into three blocks: header header input box on the top, Markdown input box on the left of the main body and Markdown instant preview box on the right of the main body.

So, you can write DOM very quickly:

render() {
  return [
    <header key='header'>
      <input type="text" placeholder="Enter article title..." spellCheck="false"/>
    </header>.<div key='main'>
      <div>
        <div contentEditable="plaintext-only"></div>
      </div>
      <div>
        <div></div>
      </div>
    </div>]}Copy the code

The structure is simple and there’s not much to say, except for the div element with the contentEditable property

There are probably three types of elements that can be used as input boxes: input, Textarea, and elements that are not false for contentEditable

The textarea can also be crossed out, leaving only the third option.

An element can be edited as long as you specify the contentEditable property and its value is not false, but most people only know what contentEditable=true means. You may not know that this property value can also be plaintext-only.

In addition, this attribute can take more than these two values. There are altogether 6 values supported by this attribute. For details about why I use plaintext-only instead of true and what the 6 values mean, please refer to the article by Zhang Xinxu. Indicates that the current editable element can only be entered in plain text, not rich text.

In addition, although the contentEditable property itself is already supported by most browsers, including IE6, compatibility is not a problem, but plaintext-only is recognized by modern browsers such as Chrome, and recognition rates are low. I don’t think this is a problem, though, since the markdown online editor is already in use, this user doesn’t have IE6 on his computer.

After completing the styles, you basically have the prototype of the editor. The input box on the left can input any content. The next step is to instantly convert the input content to the corresponding preview page.


marked

There are many plugins to convert Markdown to HTML, but I’m marked that the advantage of this plugin is that it is fast to compile, which is exactly what we need for instant preview.

Install this plug-in first, and then add it to the component:

import marked from 'marked'
Copy the code

This plugin is easy to use, just pass in the markdown text you need to compile, and then set the configuration accordingly.

The markdown text we need to pass in here is the content of the input box on the left of course, because it needs to compile immediately, so we need to listen for the input event of the input box element, and recompile the input text for each input:

<div contentEditable="plaintext-only" onInput={this.onContentChange}></div>
Copy the code

Listen for changes to the text, compile the text, and pass the compiled HTML to the right instant preview container element:

onContentChange(e) {
  this.setState({
    previewContent: marked(e.target.innerText, {breaks: true})})}Copy the code

Marked is the exposed compilation method that uses the state of previewContent to pass in content for the right-side preview container.

Note that I use innerText instead of innerHTML to get the contents of contentEditable elements. This is because if you use innerHTML, when you type special characters like >, <, etc., The final value of innerHTML automatically converts these special characters into their corresponding character entities, such as < to < > < span style = “box-sizing: border-box! Important; .

This shouldn’t be a problem, as long as it displays correctly, but we also need to marked the characters into the corresponding HTML to cause problems, which can be avoided by using innerText.

Instant build preview issues GET


Code highlighting

We might output some code sometimes, but it would be nice to have it highlighted, so I introduced the highlight.js plugin.

This plug-in can be used with marked, which is simply configured with highlight.js as a configuration item:

marked.setOptions({
  highlight (code) {
    return highlight.highlightAuto(code).value
  }
})
Copy the code

This will automatically apply highlight.js to any HTML compiled by marked, or you can configure marked yourself if you need to.

The code highlights GET


Follow the scroll

The detailed analysis of this problem has been described in another article, which is not clear enough to read.

There are only two key points to solve this problem:

  1. Correctly identify the current active scrolling container element
  2. Determines between the input box container element and the preview box container elementscrollTopThe proportion of the value
  • Correctly identify the current active scrolling container element

The mouseover event is triggered when the mouse enters the scope of an element, so you can use this event to record which container element the mouse is about to scroll.

<div className="common-container editor-container" onMouseOver={this.setCurrentIndex.bind(this, 1)} >
Copy the code
setCurrentIndex(index) {
  this.currentTabIndex = index
}
Copy the code

CurrentTabIndex = this.currenttabIndex = this.currenttabIndex = this.currenttabIndex = this.currenttabIndex = this.currenttabIndex = this.currenttabIndex = this.currenttabIndex = this.currenttabIndex = this.currenttabIndex = this.currenttabIndex = this.currenttabIndex = this.currenttabIndex = this.currenttabIndex = this.currenttabIndex = this.currenttabIndex

  • Determines between the input box container element and the preview box container elementscrollTopThe proportion of the value

The scale value can be determined according to known conditions, namely:

scale = (ch1 - ph1) / (ch2 - ph2)
Copy the code

As for what the above formula means, please refer to another article of mine, which explains it in detail.

this.scale = (this.previewWrap.offsetHeight - this.previewContainer.offsetHeight) / (this.editWrap.offsetHeight - this.editContainer.offsetHeight)
Copy the code

PreviewWrap indicates the contents of the previewContainer on the right, and previewContainer indicates the contents of the previewContainer on the right. EditWrap edits the container’s content elements for the left markDown, and editContainer edits the container elements for the left MarkDown.

Obviously, because you are in the box on the left side when the content of the input, the contents of the input box height (this) editWrap) offsetHeight) and the height of the preview box content (this) previewWrap) offsetHeight) is certainly will have corresponding change, So scale is not fixed.

Simple words, every time listening to the input box input events (such as input, delete operation will trigger the event), recalculate again scale value, it has little performance loss and fully available, but the spirit of devotion to work a technical people noble, also analyze this performance loss can actually slightly dropped to a lower.

Scale the value only when scrolling container used, so every time there is no need to change the text in the input box is recalculated once, just make sure the value is correct when rolling line, and every time don’t have to scroll to recalculate a scale, as long as the input box content did not change, use the last calculated value, You can therefore use a variable hasContentChanged to record whether the content of the identity input box has changed.

onContentChange(e) {
  this.setState({
    previewContent: marked(e.target.innerText)
  })
  !this.hasContentChanged && (this.hasContentChanged = true)}Copy the code

The simple Markdown instant preview editor is pretty much there, but if you want more sophisticated functionality, just add to it.

For example, if you want to insert an image, link an image with markdown syntax like this! [images] (https://avatars2.githubusercontent.com/u/21095835?s=460&v=4), it has nothing to do with the editor itself has, you just need to upload images saved to the server, or use a Blob spooled in browser, Then assign the address to the editor in the correct syntax.

The working sample code for this article has been posted on Github for those interested.


More and more

The markDown online + preview editor is now available. Although the function is relatively simple, it does work. If you want more complex functions, you may need to modify it yourself, such as custom search, search result highlighting, markdown input text highlighting, etc. These features are not difficult, but it is not something that can be done in a few lines of code. If you really compete with these features, then 996 will not be able to run.

But don’t worry, it’s clear that online editors have a long history. In the flying world of wheelies, an out-of-the-box editor plug-in with all the features you need already exists, and all you have to do is write a few lines of configuration.

Similar plug-ins, well-known Ace, CodeMirror and so on.

I’ve heard that if you write too long an article, you’ll fall off a cliff, so I’ve decided to save the rest for the next article, which will show you how to use Aceh and CodeMirror to build an online editor similar to this one.