将 target="_blank" 添加到 Draft.js 内容中的所有链接
Add target="_blank" to all links in Draft.js content
我正在尝试将 target="_blank"
添加到 Draft.js 内容中的所有 link。我是这个库的新手,所以我最初的尝试是简单地遍历所有实体并识别 LINK
个实体。但是,即使内容中有 link,实体映射也会变成空的。这是我的代码:
getHtml = () => {
const contentState = this.state.editorState.getCurrentContent();
// entityMap shows as empty
const entityMap = contentState.getEntityMap();
console.log('entityMap', JSON.stringify(entityMap, null, 4));
// stateToHTML() exports the anchor tag with href, but not target="_blank"
return stateToHTML(contentState);
};
如何遍历所有实体以及如何在找到 LINK 实体时插入 target="_blank"
?
P.S。我正在使用 Draft.js.
的 0.10.5 版
Link Draft.js 中的实体由 Draft.js decorators 实现。
例如从官方仓库查看the code of link-editor example:
const decorator = new CompositeDecorator([
{
strategy: findLinkEntities,
component: Link, // <== !!!
},
]);
this.state = {
editorState: EditorState.createEmpty(decorator),
showURLInput: false,
urlValue: '',
};
这里我们定义了匹配 link 实体的装饰器,并将 Link
组件传递给适当的 属性.
Here - 该组件的代码:
const Link = (props) => {
const {url} = props.contentState.getEntity(props.entityKey).getData();
return (
<a href={url} style={styles.link}>
{props.children}
</a>
);
};
所以你只需要为 a
标签添加 target="_blank"
。在这种情况下,所有 link 个实体都将使用此属性呈现。
检查工作演示:
'use strict';
const {
convertToRaw,
CompositeDecorator,
ContentState,
Editor,
EditorState,
RichUtils,
} = Draft;
class LinkEditorExample extends React.Component {
constructor(props) {
super(props);
const decorator = new CompositeDecorator([
{
strategy: findLinkEntities,
component: Link,
},
]);
this.state = {
editorState: EditorState.createEmpty(decorator),
showURLInput: false,
urlValue: '',
};
this.focus = () => this.refs.editor.focus();
this.onChange = (editorState) => this.setState({editorState});
this.logState = () => {
const content = this.state.editorState.getCurrentContent();
console.log(convertToRaw(content));
};
this.promptForLink = this._promptForLink.bind(this);
this.onURLChange = (e) => this.setState({urlValue: e.target.value});
this.confirmLink = this._confirmLink.bind(this);
this.onLinkInputKeyDown = this._onLinkInputKeyDown.bind(this);
this.removeLink = this._removeLink.bind(this);
}
_promptForLink(e) {
e.preventDefault();
const {editorState} = this.state;
const selection = editorState.getSelection();
if (!selection.isCollapsed()) {
const contentState = editorState.getCurrentContent();
const startKey = editorState.getSelection().getStartKey();
const startOffset = editorState.getSelection().getStartOffset();
const blockWithLinkAtBeginning = contentState.getBlockForKey(startKey);
const linkKey = blockWithLinkAtBeginning.getEntityAt(startOffset);
let url = '';
if (linkKey) {
const linkInstance = contentState.getEntity(linkKey);
url = linkInstance.getData().url;
}
this.setState({
showURLInput: true,
urlValue: url,
}, () => {
setTimeout(() => this.refs.url.focus(), 0);
});
}
}
_confirmLink(e) {
e.preventDefault();
const {editorState, urlValue} = this.state;
const contentState = editorState.getCurrentContent();
const contentStateWithEntity = contentState.createEntity(
'LINK',
'MUTABLE',
{url: urlValue}
);
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
const newEditorState = EditorState.set(editorState, { currentContent: contentStateWithEntity });
this.setState({
editorState: RichUtils.toggleLink(
newEditorState,
newEditorState.getSelection(),
entityKey
),
showURLInput: false,
urlValue: '',
}, () => {
setTimeout(() => this.refs.editor.focus(), 0);
});
}
_onLinkInputKeyDown(e) {
if (e.which === 13) {
this._confirmLink(e);
}
}
_removeLink(e) {
e.preventDefault();
const {editorState} = this.state;
const selection = editorState.getSelection();
if (!selection.isCollapsed()) {
this.setState({
editorState: RichUtils.toggleLink(editorState, selection, null),
});
}
}
render() {
let urlInput;
if (this.state.showURLInput) {
urlInput =
<div style={styles.urlInputContainer}>
<input
onChange={this.onURLChange}
ref="url"
style={styles.urlInput}
type="text"
value={this.state.urlValue}
onKeyDown={this.onLinkInputKeyDown}
/>
<button onMouseDown={this.confirmLink}>
Confirm
</button>
</div>;
}
return (
<div style={styles.root}>
<div style={{marginBottom: 10}}>
Select some text, then use the buttons to add or remove links
on the selected text.
</div>
<div style={styles.buttons}>
<button
onMouseDown={this.promptForLink}
style={{marginRight: 10}}>
Add Link
</button>
<button onMouseDown={this.removeLink}>
Remove Link
</button>
</div>
{urlInput}
<div style={styles.editor} onClick={this.focus}>
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
placeholder="Enter some text..."
ref="editor"
/>
</div>
<input
onClick={this.logState}
style={styles.button}
type="button"
value="Log State"
/>
</div>
);
}
}
function findLinkEntities(contentBlock, callback, contentState) {
contentBlock.findEntityRanges(
(character) => {
const entityKey = character.getEntity();
return (
entityKey !== null &&
contentState.getEntity(entityKey).getType() === 'LINK'
);
},
callback
);
}
const Link = (props) => {
const {url} = props.contentState.getEntity(props.entityKey).getData();
return (
<a href={url} target="_blank" style={styles.link}>
{props.children}
</a>
);
};
const styles = {
root: {
fontFamily: '\'Georgia\', serif',
padding: 20,
width: 600,
},
buttons: {
marginBottom: 10,
},
urlInputContainer: {
marginBottom: 10,
},
urlInput: {
fontFamily: '\'Georgia\', serif',
marginRight: 10,
padding: 3,
},
editor: {
border: '1px solid #ccc',
cursor: 'text',
minHeight: 80,
padding: 10,
},
button: {
marginTop: 10,
textAlign: 'center',
},
link: {
color: '#3b5998',
textDecoration: 'underline',
},
};
ReactDOM.render(
<LinkEditorExample />,
document.getElementById('react-root')
);
body {
font-family: Helvetica, sans-serif;
}
.public-DraftEditor-content {
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.0/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/draft-js/0.7.0/Draft.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/draft-js/0.10.0/Draft.js"></script>
<div id="react-root"></div>
draft-js-export-html stateToHTML() 允许您传递选项参数来更改实体对象的形状。如果您可以将 target='_blank' 添加到所有锚标记,您可以这样做:
...
let options = {
entityStyleFn: (entity) => {
const entityType = entity.get('type').toLowerCase();
if (entityType === 'link') {
const data = entity.getData();
return {
element: 'a',
attributes: {
href: data.url,
target:'_blank'
},
style: {
// Put styles here...
},
};
}
}
};
return stateToHTML(contentState, options);
如果你使用了draft-js-anchor-plugin,你只能在展示内容的时候加上linkTarget属性。代码如下。
const linkPlugin = createLinkPlugin({linkTarget: '_blank'});
我正在尝试将 target="_blank"
添加到 Draft.js 内容中的所有 link。我是这个库的新手,所以我最初的尝试是简单地遍历所有实体并识别 LINK
个实体。但是,即使内容中有 link,实体映射也会变成空的。这是我的代码:
getHtml = () => {
const contentState = this.state.editorState.getCurrentContent();
// entityMap shows as empty
const entityMap = contentState.getEntityMap();
console.log('entityMap', JSON.stringify(entityMap, null, 4));
// stateToHTML() exports the anchor tag with href, but not target="_blank"
return stateToHTML(contentState);
};
如何遍历所有实体以及如何在找到 LINK 实体时插入 target="_blank"
?
P.S。我正在使用 Draft.js.
的 0.10.5 版Link Draft.js 中的实体由 Draft.js decorators 实现。
例如从官方仓库查看the code of link-editor example:
const decorator = new CompositeDecorator([
{
strategy: findLinkEntities,
component: Link, // <== !!!
},
]);
this.state = {
editorState: EditorState.createEmpty(decorator),
showURLInput: false,
urlValue: '',
};
这里我们定义了匹配 link 实体的装饰器,并将 Link
组件传递给适当的 属性.
Here - 该组件的代码:
const Link = (props) => {
const {url} = props.contentState.getEntity(props.entityKey).getData();
return (
<a href={url} style={styles.link}>
{props.children}
</a>
);
};
所以你只需要为 a
标签添加 target="_blank"
。在这种情况下,所有 link 个实体都将使用此属性呈现。
检查工作演示:
'use strict';
const {
convertToRaw,
CompositeDecorator,
ContentState,
Editor,
EditorState,
RichUtils,
} = Draft;
class LinkEditorExample extends React.Component {
constructor(props) {
super(props);
const decorator = new CompositeDecorator([
{
strategy: findLinkEntities,
component: Link,
},
]);
this.state = {
editorState: EditorState.createEmpty(decorator),
showURLInput: false,
urlValue: '',
};
this.focus = () => this.refs.editor.focus();
this.onChange = (editorState) => this.setState({editorState});
this.logState = () => {
const content = this.state.editorState.getCurrentContent();
console.log(convertToRaw(content));
};
this.promptForLink = this._promptForLink.bind(this);
this.onURLChange = (e) => this.setState({urlValue: e.target.value});
this.confirmLink = this._confirmLink.bind(this);
this.onLinkInputKeyDown = this._onLinkInputKeyDown.bind(this);
this.removeLink = this._removeLink.bind(this);
}
_promptForLink(e) {
e.preventDefault();
const {editorState} = this.state;
const selection = editorState.getSelection();
if (!selection.isCollapsed()) {
const contentState = editorState.getCurrentContent();
const startKey = editorState.getSelection().getStartKey();
const startOffset = editorState.getSelection().getStartOffset();
const blockWithLinkAtBeginning = contentState.getBlockForKey(startKey);
const linkKey = blockWithLinkAtBeginning.getEntityAt(startOffset);
let url = '';
if (linkKey) {
const linkInstance = contentState.getEntity(linkKey);
url = linkInstance.getData().url;
}
this.setState({
showURLInput: true,
urlValue: url,
}, () => {
setTimeout(() => this.refs.url.focus(), 0);
});
}
}
_confirmLink(e) {
e.preventDefault();
const {editorState, urlValue} = this.state;
const contentState = editorState.getCurrentContent();
const contentStateWithEntity = contentState.createEntity(
'LINK',
'MUTABLE',
{url: urlValue}
);
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
const newEditorState = EditorState.set(editorState, { currentContent: contentStateWithEntity });
this.setState({
editorState: RichUtils.toggleLink(
newEditorState,
newEditorState.getSelection(),
entityKey
),
showURLInput: false,
urlValue: '',
}, () => {
setTimeout(() => this.refs.editor.focus(), 0);
});
}
_onLinkInputKeyDown(e) {
if (e.which === 13) {
this._confirmLink(e);
}
}
_removeLink(e) {
e.preventDefault();
const {editorState} = this.state;
const selection = editorState.getSelection();
if (!selection.isCollapsed()) {
this.setState({
editorState: RichUtils.toggleLink(editorState, selection, null),
});
}
}
render() {
let urlInput;
if (this.state.showURLInput) {
urlInput =
<div style={styles.urlInputContainer}>
<input
onChange={this.onURLChange}
ref="url"
style={styles.urlInput}
type="text"
value={this.state.urlValue}
onKeyDown={this.onLinkInputKeyDown}
/>
<button onMouseDown={this.confirmLink}>
Confirm
</button>
</div>;
}
return (
<div style={styles.root}>
<div style={{marginBottom: 10}}>
Select some text, then use the buttons to add or remove links
on the selected text.
</div>
<div style={styles.buttons}>
<button
onMouseDown={this.promptForLink}
style={{marginRight: 10}}>
Add Link
</button>
<button onMouseDown={this.removeLink}>
Remove Link
</button>
</div>
{urlInput}
<div style={styles.editor} onClick={this.focus}>
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
placeholder="Enter some text..."
ref="editor"
/>
</div>
<input
onClick={this.logState}
style={styles.button}
type="button"
value="Log State"
/>
</div>
);
}
}
function findLinkEntities(contentBlock, callback, contentState) {
contentBlock.findEntityRanges(
(character) => {
const entityKey = character.getEntity();
return (
entityKey !== null &&
contentState.getEntity(entityKey).getType() === 'LINK'
);
},
callback
);
}
const Link = (props) => {
const {url} = props.contentState.getEntity(props.entityKey).getData();
return (
<a href={url} target="_blank" style={styles.link}>
{props.children}
</a>
);
};
const styles = {
root: {
fontFamily: '\'Georgia\', serif',
padding: 20,
width: 600,
},
buttons: {
marginBottom: 10,
},
urlInputContainer: {
marginBottom: 10,
},
urlInput: {
fontFamily: '\'Georgia\', serif',
marginRight: 10,
padding: 3,
},
editor: {
border: '1px solid #ccc',
cursor: 'text',
minHeight: 80,
padding: 10,
},
button: {
marginTop: 10,
textAlign: 'center',
},
link: {
color: '#3b5998',
textDecoration: 'underline',
},
};
ReactDOM.render(
<LinkEditorExample />,
document.getElementById('react-root')
);
body {
font-family: Helvetica, sans-serif;
}
.public-DraftEditor-content {
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.0/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/draft-js/0.7.0/Draft.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/draft-js/0.10.0/Draft.js"></script>
<div id="react-root"></div>
draft-js-export-html stateToHTML() 允许您传递选项参数来更改实体对象的形状。如果您可以将 target='_blank' 添加到所有锚标记,您可以这样做:
...
let options = {
entityStyleFn: (entity) => {
const entityType = entity.get('type').toLowerCase();
if (entityType === 'link') {
const data = entity.getData();
return {
element: 'a',
attributes: {
href: data.url,
target:'_blank'
},
style: {
// Put styles here...
},
};
}
}
};
return stateToHTML(contentState, options);
如果你使用了draft-js-anchor-plugin,你只能在展示内容的时候加上linkTarget属性。代码如下。
const linkPlugin = createLinkPlugin({linkTarget: '_blank'});