市面上大多数的富文本编辑器都是现成的,很难根据自己的需求进行无论是功能亦或是渲染格式的修改。
而由脸书开源的这款draft.js在富文本编辑器中简直是一股清流般的存在。draft在英文中是“草稿”的意思,如它名字所言,它并不是一款现成的富文本编辑器,而是一款富文本编辑器框架,这意味着你可以在此基础上进行二次开发,写出适合你自己应用场景的富文本编辑器。
下面会写出一些我个人对这款富文本编辑器的实践路线。
(一)安装:
npm install --save draft-js
(二)初始化一个draft编辑器的实例:
//Editor.js
//Editor是一个自定义的组件
import React, {findDOMNode, Component} from 'react';
import {
convertFromRaw,
convertToRaw,
CompositeDecorator,
DefaultDraftBlockRenderMap,
ContentState,
Editor,
EditorState,
Entity,
RichUtils,
getDefaultKeyBinding,
KeyBindingUtil,
Modifier
} from 'draft-js';
import style from './css.css';
//这里使用了css-Moudles
export default class componentName extends Component{
constructor(props){
super(props);
this.state = {
editorState:EditorState.createEmpty()
};
this.focus = () => this.refs.editor.focus();
this.onChange = (editorState) => this.setState({editorState});
}
render(){
const {editorState} = this.state;
const {
} = this.props;
return(
this.onChange}
ref="editor"
>
)
}
}
这样,就能渲染出一个最简单的文本编辑器,你现在可以在里面输入点什么。
(三)对编辑器进行样式修改。
我的做法是,在
的最外层加几个样式DIV。
<div className={style.editorRoot} onClick={this.focus}>
<Editor
editorState={editorState}
onChange = {this.onChange}
blockStyleFn={getBlockStyle}
ref="editor"
>
Editor>
div>
(四)增加格式按钮:光能输入不行,得有什么东西来控制输入文字的格式。按钮和编辑器是分开的,所以我写了一个编辑器按钮的组件,让这些组件与
同级。
<div className={style.init}>
<div className={style.operate}>
<InlineStyle/>
<BlockStyle/>
<ColorStyle/>
div>
<div className={style.editorRoot} onClick={this.focus}>
<Editor
editorState={editorState}
onChange = {this.onChange}
ref="editor"
>
Editor>
div>
<Post storeHandle={this.storeHandle}>保存Post>
div>
但是现在只有格式,按钮并没有具体的功能,下面我们把功能加上去。
功能无非就是为输入的文字加上相应的格式。
一共有两种格式:行和块。
(五)行样式渲染以及修改: 这里最好分成几个小步骤
RichUtils.toggleBlockType()
方法获取新的editorState
上添加映射类开始步骤:
1)确定行格式需要的功能以及对应的类名:
// 行按钮,关键属性 styleName
//这个数组可以用于渲染按钮,在点击事件中把styleName传给父方法_toggleInlineStyle()
const InlineType = [
{key:1,label:'B',styleName:'Bold',title:'加粗',iconClassName:''},
{key:2,label:'I',styleName:'Italic',title:'斜体',iconClassName:''},
];
2)确定行样式的映射类:
//行样式映射
const editorStyleMap = {
//字体
Bold:{
fontWeight: '600',
},
Italic:{
fontStyle: 'italic',
},
};
3)使用RichUtils.toggleBlockType()
方法获取新的editorState:
//接受按钮传过来的styleName, RichUtils.toggleInlineStyle()返回一个新的editorState.使用this.onChange()进行更新。
_toggleInlineStyle = (inlineStyle)=>{
this.onChange(
RichUtils.toggleInlineStyle(
this.state.editorState,
inlineStyle
)
)
};
4)在组件
上添加映射类:
<Editor
customStyleMap = {editorStyleMap}
editorState={editorState}
onChange = {this.onChange}
ref="editor"
>
Editor>
这样,当你选中一段文字,再点击相应的格式按钮时,你选中的文字内容就会被加上相应的css样式。
(六)默认块以及默认块样式修改:
draft.js提供了几个默认块的styleName以及对应渲染的元素:
也就是说,如果styleName使用了右侧这些名字,他们会被渲染成左侧这些标签。而不是行元素的span。
还是分成几个步骤吧:
1. 确定行格式需要的功能以及对应的类名
2. 使用RichUtils.toggleBlockType()
方法获取新的editorState
1)确定行格式需要的功能以及对应的类名:
//和行样式一样,可以用于渲染块格式按钮。
//在点击事件中,把styleName传给父的RichUtils.toggleBlockType()中。
//有一点必须注意,这里的styleName只能是Draft.js默认的样式名,也就是上图右侧的名字。因为我们现在就是使用默认块。
const BlockType = [
{key:1,label: 'H', styleName: 'header-two',title:'小标题',iconClassName:''},
{key:2,label: '“ ”', styleName: 'blockquote',title:'引用',iconClassName:''},
{key:3,label: '>', styleName: 'code-block',title:'代码块',iconClassName:''},
{key:4,label: '', styleName: 'unordered-list-item',title:'有序列表',iconClassName:'iconfont icon-other editorButtonIcon'},
{key:5,label: '', styleName: 'ordered-list-item',title:'无序列表',iconClassName:'iconfont icon-other editorButtonIcon'},
];
2)使用RichUtils.toggleBlockType()
方法获取新的editorState
//块格式
//按钮把styleName传给这个方法,得到新的editorState并更新。
_toggleBlockType = (blockType)=>{
this.onChange(
RichUtils.toggleBlockType (
this.state.editorState,
blockType
)
);
};
这样就成功使用了draft.js默认的块格式了。下面是默认格式blockquote的效果图:
也许你觉得默认格式太丑了,我想修改一下。draft.js的组件
提供了一个属性blockStyleFn,就是用来读取自定义样式的。
上增加属性blockStyleFn。1)在css中自定义格式。
//css
//我使用了css-Moudles,所以加上了:global()
:global(.RichEditor-blockquote){
display:block;
border-left: 5px solid #d2d2d2;
color: #666;
margin: 0 0;
padding: 5px 20px;
font-size: 15px;
font-style: italic;
background-color: #eff9ff;
}
2)声明一个方法,把自定义格式的css和styleName对应上:
function getBlockStyle(blockName){
switch(blockName.getType()){
case 'blockquote' :
return 'RichEditor-blockquote';
default:
return null;
}
}
2)在
上增加属性blockStyleFn:
{editorStyleMap}
editorState={editorState}
onChange = {this.onChange}
blockStyleFn={getBlockStyle}
ref="editor"
/>
在css中可以发现,我把背景色由原来的灰色换成了浅蓝色。现在引用格式长这样:
(七)格式以及对应格式按钮之间的高亮联系:
draft.js的demo给出了当光标在有格式的文本时对应的格式按钮高亮的实现方法。在这里稍微介绍一下。
核心思路是,通过editorState
这个对象的一些方法获取当前光标所在的“标签”(也就是“格式”)的styleName,然后和按钮的styleName进行对比,如果===,则表示现在光标所在的文本格式中有用到这个styleName。这时加上对应的className即可。
行和块获取当前的styleName的方法是不一样的。
1)行:
//按钮组件与editor组件有一层中介组件InlineStyleControl组件,用以渲染按钮。
//@InlineType就是由父组件Editor传进来的那个行按钮格式数组。
render(){
const {
editorState,
InlineType
} = this.props;
//获取所有的行样式名
const inlineType = editorState.getCurrentInlineStyle();
return(
.init}>
{InlineType.map((obj)=>{
return .key}
//进行对比。
active={inlineType.has(obj.styleName)}
title={obj.title}
label={obj.label}
iconClassName = {obj.iconClassName}
styleName={obj.styleName}
onToggle = {this.props.onToggle}
/>
})}
)
}
//按钮组件ButtonType的render
render(){
const {
title,
label,
active,
iconClassName,
} = this.props;
let classNames;
if(active){
//有的话,加上这个CSS类名
classNames = 'editorActiveButton'
}
return(
' ' + classNames + ' ' + iconClassName}
title={title}
onClick = {this.onToggle}
>
{label}
)
}
现在的效果是这样,当光标在斜体内容里时,斜体按钮”I”会有红色高亮。
块格式实现相同的效果和行格式有一点点区别。区别在获取当前有哪些格式的方法上。
//BlockStyleControl组件
render(){
const {
editorState,
BlockType
} = this.props;
// 这里开始获取当前光标所在的格式的类名
let selection = editorState.getSelection();
let blockStyle = editorState
.getCurrentContent()
.getBlockForKey(selection.getStartKey())
.getType();
return(
.init}>
{BlockType.map((obj)=>{
return .key}
//检查
active = {blockStyle === obj.styleName}
title={obj.title}
label={obj.label}
iconClassName = {obj.iconClassName}
styleName={obj.styleName}
onToggle = {this.props.onToggle}
/>
})}
)
}
有了这个小功能。用户就可以再点击一次按钮,把格式取消或者加上。