React H5 uses div to customize a simple rich text editor

Recently, the h5 end of the project is to upload graphics and text, but also to support user input format, such as line change, so using input control to save input content, picture upload control is not appropriate, because it is difficult to know the user's input style.

If you use some of the existing rich text editors, it seems not very cost-effective, so considering the use of div to achieve their own is an ideal solution.

Consider first what problems need to be solved if you use div to implement a simple rich text editor.

First of all, div is not editable by default and needs to be set up to edit it. This is very simple and only needs to be used.

   contentEditable="true"

Secondly, we need to insert the image at the position specified by div. The position specified is the position of the cursor. When we insert the image, we need to know the position of the cursor. We need to search online for a simple use directly on the getSelection API W3Cschool. Interested children's shoes can be seen.

Get the current cursor selection position

let selection = window.getSelection();
let range = selection.getRangeAt(0);

So knowing the cursor position, how to insert the picture?

 let img = document.createElement("img");
   img.src = json.url;
   //Maximum width is mobile phone width minus 20
   img.style.maxWidth = (width-20) + 'px';
   range.insertNode(img);//Picture insertion at selected location   

Is that all?

Of course, it's not over yet. Another problem encountered in the implementation is the change of focus. If I want to insert an image, I need to upload the image first, but when I click on the button to upload the image, the focus has changed. At this time, the selected position is where you click!

So how to solve this problem?

Careful analysis, when clicking, when the focus changes, we need to record the location of the last focus, where was the last focus? In editable divs, when the focus changes, the editable div loses focus!

Is it possible to monitor div out of focus? Of course the answer is yes!

 onBlur={() => {
   let selection = window.getSelection();
   range = selection.getRangeAt(0);
   }}

Is it easy to implement onBlur monitoring for div?

Next, take a look at the complete code

import React, {Component} from "react";
import "../../assets/css/topic/DivEdit.css";
import "../../assets/common/second-common.css";
​
let range;
let width = document.documentElement.clientWidth;
//Headbar
class DivEdit extends Component {
    constructor(props) {
        super(props);
        let typeArr = [".png", ".jpg", ".jpeg", ".bmp", ".gif"];
        this.state = {
            id:'',
            inputValueHtml: '',
            fileType: typeArr,
            showTips:true
        }
    }
​
​
    //Cursor position at the beginning of editable div
    setStartFocus() {
​
        document.querySelector('#my-question-define-edit').focus();
​
    }
​
​
    //The cursor is located at the end of the content
    setEndFocus() {
​
​
        let srcObj = document.querySelector('#my-question-define-edit');
        let selection = window.getSelection();
        range = document.createRange();
        range.selectNodeContents(srcObj);
        selection.removeAllRanges();
        selection.addRange(range);
        range.setStart(srcObj, 1);
        range.setEnd(srcObj, 1);
​
    }
​
​
    // input Selection File
    triggerSelect() {
        return document.getElementById('topic-add-img').click();
    }
​
​
    //Processing uploaded files
    handleChange(file) {
        // Judge the file type and upload it
        if (window.typeMatch(this.state.fileType, file.name)) {
            // Upload files
            this.saveFile(file);
        } else {
            Toast.fail("File type does not match, please re-select", 3);
        }
    }
​
    // Upload files
    saveFile(file) {
     
     //Handling uploads by yourselves/ when it's successful
    
             let img = document.createElement("img");
             img.src = json.url;
             img.style.maxWidth = (width-20) + 'px';
​
            if(range) {
​
                  range.insertNode(img);
​
           }else{
​
                this.setStartFocus();
                let selection = window.getSelection();
                range = selection.getRangeAt(0);
                range.insertNode(img);
​
              }
   
    }
​
    componentDidMount() {
        let question=this.props.question;
        this.setState({
        inputValueHtml: '<p>' + question + '</p><br/><br/><br/>',showTips:!(question&&question!=='')
        }, 
        () => {
​
            if(question&&question!==''){
​
                this.setEndFocus();
​
            }
        });
​
    }
​
​
    getHtml(){
​
        return this._editDiv.innerHTML;
​
    }
​
​
    render() {
​
​
        return (
​
            <div className={'add-my-question-first-edit'} >
​
                <div id={'my-question-define-edit'}
                     onFocus={() => {
​
                         this.setState({showTips:false});
​
                     }}
                     onBlur={() => {
                         let selection = window.getSelection();
                         range = selection.getRangeAt(0);
                     }}
                     onClick={() => {
​
                         let selection = window.getSelection();
                         range = selection.getRangeAt(0);
​
                     }}
                     ref={(editDiv) => this._editDiv = editDiv}
                     className={'add-my-question-edit'}
                     contentEditable="true"
                     dangerouslySetInnerHTML={{__html: this.state.inputValueHtml}}/>
​
                {
​
                    this.state.showTips?
                        <span style={{
                        position: 'absolute',
                        top:10,
                        left:10,
                        marginTop:'1.333rem',
                        fontSize:17,color:'#E8E8E8'
                        }}>Enter your questions...</span>:null
​
                }
​
                <div className={'add-bottom-menu'}>
​
                    <img style={{width: 20, position: 'absolute', left: 10}}
                         src={require("../../assets/images/topic_topic_show.png")}
                         alt='Arrow'/>
​
​
                    <div className={'topic-add-pic'}
                         onClick={() => {
                              //Pick up the uploaded picture
                             this.triggerSelect();
                             
                         }}>
                        <img style={{width: 20}}
                             src={require("../../assets/images/topic_upload_pic.png")}
                             alt='Upload pictures'/>
​
                        <span style={{marginLeft: 10, fontSize: 15}}>Upload pictures</span>
                    </div>
​
​
                </div>
                
                <div style={{visibility: 'hidden'}}>
                    <input
                        type="file"
                        id={'topic-add-img'}
                        style={{display: "none"}}
                        onChange={e => this.handleChange(e.target.files[0])}
                    />
                </div>
​
            </div>
​
        );
    }
}
​
export default DivEdit;​

style sheet

.add-my-question-first-edit {
    display: flex;
    flex-direction: column;
    flex: 1;
​
 }
​
.add-my-question-first-edit  .add-my-question-edit{
​
    flex: 1;
    margin-top: 1.333rem;
    /*margin-bottom: 1.333rem;*/
    font-size: 0.453rem;
    background-color: #fff;
    padding: 10px 10px 1.867rem;
​
}
​
.add-my-question-first-edit  .add-bottom-menu{
​
    position: fixed;
    bottom: 0;
    width: 100%;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    height: 1.067rem;
    background-color: #fff;
    border-top: #E8E8E8 solid 1px;
​
​
​
}
​
.add-my-question-first-edit  .add-bottom-menu .topic-add-pic{
​
    display: flex;
    flex-direction: row;
    align-items: center;
    align-self: center;
​
}

More content, please pay attention to the author's two-dimensional code synchronously!

Keywords: JSON React Mobile

Added by MrPotatoes on Fri, 23 Aug 2019 16:21:11 +0300