87,997
社区成员




React.js
萌新刚入职,碰到一个抽象的需求,要在内容输入框中写文字的时候点击边上的其他标签可以插入到内容中的光标位置,翻看了许多网页都没有想要的解决方法,综合了一下,结合Range和 window.getSelection();整理了如下解决方法。
1、render中写组件
<div
id="divtextarea"
placeholder="请输入您的内容"
contentEditable
style={{ height: '150px', border: '1px solid #d9d9d9', padding: ' 0 2px', outline: 'none', overflowY: 'auto' }}
tabIndex="0"
onFocus={this.handleChange}
/>
2、onFocus方法代替textarea的onchange方法
// 监听光标变化代替onChange
handleChange = () => {
document.onselectionchange = () => {
let sel = null;
let value = document.getElementById('divtextarea').innerHTML;
value = value.replace(/<span.*?>/g, '${');
value = value.replace(/<\/span.*?>/g, '}');
if (document.selection) {
sel = document.selection;
} else {
sel = window.getSelection();
// 记录光标位置判断是否在div上
if (sel.rangeCount > 0 && (sel.anchorNode.id === 'divtextarea' || sel.anchorNode.parentNode.id === 'divtextarea')) {
this.setState({
nrValue: value,
});
const { changezt } = this.props;
if (changezt) {
changezt(value);
}
console.log(value);
}
}
};
}
3、写标签插入的方法
// 往内容中插入span标签
insertContent = (content) => {
if (!content) {
return;
}
let sel = null;
if (document.selection) {
sel = document.selection;
sel.createRange().pasteHTML(content);
} else {
sel = window.getSelection();
// 同上考虑到标签插入时光标会在之前插入过的标签内或者不在DIV上,这里作一下判断
// 光标在div的text上时后层组件是div,在插入标签时后两层是div,这样就不会插入到div外面或者出现标签内插入标签。
if (sel.rangeCount > 0 && (sel.anchorNode.id === 'divtextarea' || sel.anchorNode.parentNode.id === 'divtextarea')) {
const range = sel.getRangeAt(0);
range.deleteContents();
const el = document.createElement('span');
el.innerHTML = content;
const frag = document.createDocumentFragment();
const node = el.firstChild;
const lastNode = frag.appendChild(node);
range.insertNode(frag);
const contentRange = range.cloneRange();
contentRange.setStartAfter(lastNode);
contentRange.collapse(true);
sel.removeAllRanges();
sel.addRange(contentRange);
}
}
}
4、调用(在需要调用的方法里面加入下面两行代码就行了)
const imgTag = `<span contenteditable="false" style="background-color: #e8e8e8; border-radius: 4px; margin : 0 3px">${需要插入的内容}</span>`;
this.insertContent(imgTag);
// 考虑到后台保存数据时不能出现标签,做一个正则替换
let value = document.getElementById('divtextarea').innerHTML;
value = value.replace(/<span.*?>/g, '${');
value = value.replace(/<\/span.*?>/g, '}');
value = value.replace(/<br>/g, '');
因为使用的是window.getSelectionfang方法获取光标,所以避免标签乱插入,考虑过给插入的span标签加入user-select: none;属性来防止光标选中,但是contenteditable="true" 属性的标签中加这个属性会出现bug,当最后连续插入标签的时候删除会直接清空div所有内容,所以这里用光标后层标签是否是divtextarea来代替。