手寫 editor web 富文本編輯器

手寫富文本編輯器,須要使用 react 中自帶的 FragmentcreateRef,以及 execCommand 方法,這兩個到底有什麼做用呢。那麼,在演示代碼前先了解下 FragmentcreateRefexecCommand 是什麼,分別有什麼做用?javascript

Fragment

React 中一個常見模式是 一個組件返回多個元素。Fragment 至關於一個 React 組件,它能夠聚合一個子元素列表,而且不在 DOM 中增長額外節點。在 react 中返回的元素必須有父元素進行包裹,但特殊狀況下,咱們不想使用多餘的標籤,此時可使用 Fragment 包裹標籤。Fragment 更像是一個空的 jsx 標籤 <></>css

class FragmentDemo extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            list: [
                {
                    type: '姓名',
                    text: 'wqjiao'
                },
                {
                    type: '性別',
                    text: '女'
                }
            ]
        }
    }

    render () {
        let { list } = this.state;

        return (
            <table>
                <tbody>
                    <tr>
                        { list && list.map(item => {
                            return (
                                <React.Fragment key={'list' + item}>
                                    <td>{ item.type }</td>
                                    <td>{ item.text }</td>
                                </React.Fragment>
                            )
                        }) }
                    </tr>
                </tbody>
            </table>
        )
    }
}

其中,key 是惟一能夠傳遞給 Fragment 的屬性。html

createRef

React 官網中是這麼解釋 Refs and the DOMjava

Refs are created using React.createRef() and attached to React elements via the ref attribute. 
Refs are commonly assigned to an instance property when a component is constructed so they can
be referenced throughout the component.

使用 React.createRef() 建立 refs,經過 ref 屬性來得到 React 元素。當構造組件時,refs 一般被賦值給實例的一個屬性,這樣你能夠在組件中任意一處使用它們。經過 current 屬性取得 DOM 節點react

execCommand

當一個 HTML 文檔切換到設計模式時,document 暴露 execCommand 方法,該方法容許運行命令來操縱可編輯內容區域的元素。web

大多數命令影響 document 的 selection(粗體,斜體等),當其餘命令插入新元素(添加連接)或影響整行(縮進)。當使用 contentEditable 時,調用 execCommand() 將影響當前活動的可編輯元素。設計模式

可是 document.execCommand(xxxx) 是 IE 獨家提供的,有些功能在 Chrome/FrieFox 中是不支持的,好比 粘貼功能 document.execCommand("paste", "false", null)數組

富文本編輯器

在瞭解以上兩個 React 屬性以後,附上手寫 editor web 富文本編輯器的 js 代碼antd

  • js 代碼
import React, { Component, Fragment, createRef } from "react";
import { Select } from 'antd';
import './index.less';

const Option = Select.Option;

class WqjiaoEditor extends Component {
    
    constructor(props) {
        super(props);
        this.state = {
            editorIcons: [{
                id: 'choose-all',
                text: '全選',
                event: this.chooseAll
            }, {
                id: 'copy',
                text: '複製',
                event: this.copy
            }, {
                id: 'cut',
                text: '剪切',
                event: this.cut
            }, {
                id: 'bold',
                text: '加粗',
                event: this.bold
            }, {
                id: 'italic',
                text: '斜體',
                event: this.italic
            }, {
                id: 'font-size',
                text: '字體大小',
                event: this.fontSize
            }, {
                id: 'underline',
                text: '下劃線',
                event: this.underline
            }, {
                id: 'background-color',
                text: '背景色',
                event: this.backgroundColor
            }],
            fontSizeOption: [],
            isShow: false,
            fontSize: '7'
        }
    }

    document = createRef(null);

    componentDidMount() {
        this.editor = this.document.current.contentDocument;
        this.editor.designMode = 'On';
        this.editor.contentEditable = true;

        let fontSizeOption = [];
        // 字體大小數組
        for (let i = 1; i <= 7; i ++) {
            fontSizeOption.push(i);                                                
        }

        this.setState({
            fontSizeOption
        });
    }

    // 全選
    chooseAll = () => {
        this.editor.execCommand('selectAll');
    }

    // 複製
    copy = () => {
        this.editor.execCommand('copy');
    }

    // 剪切
    cut = () => {
        this.editor.execCommand('cut');
    }

    // 加粗
    bold = () => {
        this.editor.execCommand('bold');
    }

    // 斜體
    italic = () => {
        this.editor.execCommand('italic');
    }

    // 字體大小
    fontSize = () => {
        let me = this;     
    }

    onClick(id) {
        if (id === 'font-size') {
            this.setState({
                isShow: true
            });
        }
    }

    onChange(value) {
        this.setState({
            fontSize: value,
            isShow: false
        })
        this.editor.execCommand('fontSize', true, value);
    }

    // 下劃線
    underline = () => {
        this.editor.execCommand('underline');
    }

    // 背景色
    backgroundColor = () => {
        this.editor.execCommand('backColor', true, '#e5e5e5');
    }

    render() {
        let me = this;
        let { editorIcons, isShow, fontSize, fontSizeOption } = me.state;

        return (
            <Fragment>
                <div className="wqjiao-editor">
                    <div className="wqjiao-editor-icon">
                        <ul className="wqjiao-icon-list clearfix">
                            { editorIcons && editorIcons.map((item, index) => {
                                return (
                                    <li
                                        className="wqjiao-icon-item"
                                        onClick={item.event}
                                        key={'editor' + index}
                                    >
                                        <i
                                            className={"wqjiao-i i-" + item.id}
                                            title={item.text}
                                            alt={item.text}
                                            onClick={me.onClick.bind(me, item.id)}
                                        />
                                        { (item.id === 'font-size' && isShow) && <div className="wqjiao-editor-select">
                                            <Select
                                                value={fontSize}
                                                onChange={me.onChange.bind(me)}
                                            >
                                                { fontSizeOption && fontSizeOption.map((i, k) => {
                                                    return (
                                                        <Option
                                                            key={'fontSize' + k}
                                                            value={i}
                                                        >{i}</Option>
                                                    );
                                                }) }
                                            </Select>
                                        </div> }
                                    </li>
                                );
                            }) }
                        </ul>
                    </div>
                    <iframe ref={this.document} className="wqjiao-editor-textarea"></iframe>
                </div>
            </Fragment>
        )
    }
}

export default WqjiaoEditor;
  • css 樣式,圖片本地添加
// wqjiao editor web 富文本編輯器
::-webkit-scrollbar {
    display: none;
}

.wqjiao-editor {
    // width: 100%;
    width: 300px;
    
    // 樣式重置
    * {
        margin: 0;
        padding: 0;
        list-style: none;
        font-style: normal;
    }

    // editor 圖標
    .wqjiao-editor-icon {
        width: 100%;
        border: 1px solid #e5e5e5;
        border-top-left-radius: 4px;
        border-top-right-radius: 4px;
        box-sizing: border-box;
        .wqjiao-icon-list {
            padding: 0 5px;
        }
        .wqjiao-icon-item {
            float: left;
            font-size: 10px;
            padding: 5px 10px;
            position: relative;
        }
        .wqjiao-editor-select {
            position: absolute;
            top: 20px;
            left: -3px;
            width: 40px;
        }
        .ant-select {
            width: 100%;
        }
        .ant-select-selection__rendered,
        .ant-select-selection--single {
            height: 20px;
            line-height: 20px;
        }
        .ant-select-arrow {
            top: 4px;
            right: 4px;
        }
        .ant-select-selection-selected-value {
            padding: 0;
        }
        .ant-select-selection__rendered {
            margin: 0;
            margin-left: 4px;
        }
    }

    // editor 文本區域
    .wqjiao-editor-textarea {
        width: 100%;
        min-height: 200px;
        font-size: 14px;
        padding: 10px;
        border: 1px solid #e5e5e5;
        border-top: none;
        border-bottom-left-radius: 4px;
        border-bottom-right-radius: 4px;
        box-sizing: border-box;
        &:hover,
        &:focus {
            outline: none;
            box-shadow: none;
        }
    }

    // 清除浮動元素帶來的影響
    .clearfix {
        zoom: 1;
    }
    .clearfix:after {
        content: "";
        clear: both;
        height: 0;
        visibility: hidden;
        display: block;
    }

    // 圖標背景
    .wqjiao-i {
        display: block;
        width: 14px;
        height: 14px;
        cursor: pointer;
        &.i-choose-all {
           background: url('./img/i-choose-all.png');
        }
        &.i-copy {
           background: url('./img/i-copy.png'); 
        }
        &.i-cut {
           background: url('./img/i-cut.png'); 
        }
        &.i-bold {
            background: url('./img/i-bold.png'); 
        }
        &.i-italic {
            background: url('./img/i-italic.png'); 
        }
        &.i-font-size {
            background: url('./img/i-font-size.png'); 
        }
        &.i-underline {
            background: url('./img/i-underline.png'); 
        }
        &.i-background-color {
            background: url('./img/i-background-color.png'); 
        }
        &:hover,
        &.active {
            &.i-choose-all {
                background: url('./img/i-choose-all-active.png');
            }
            &.i-copy {
                background: url('./img/i-copy-active.png'); 
            }
            &.i-cut {
                background: url('./img/i-cut-active.png'); 
            }
            &.i-bold {
                background: url('./img/i-bold-active.png'); 
            }
            &.i-italic {
                background: url('./img/i-italic-active.png'); 
            }
            &.i-font-size {
                background: url('./img/i-font-size-active.png'); 
            }
            &.i-underline {
                background: url('./img/i-underline-active.png'); 
            }
            &.i-background-color {
                background: url('./img/i-background-color-active.png'); 
            }
        }
    }
}
  • 效果演示
  • execCommand 兼容性
相關文章
相關標籤/搜索