contenteditable
修改元素的可编辑性,使之可以编辑,html就给我们提供了这么个属性,要知道在一个普通元素内插入一个标签要比在textarea中要容易多了。
-webkit-user-modify
- read-only: 默认值,元素只读,不可编辑;
- read-write: 可以编辑,支持富文本;
- read-write-plaintext-only: 可以编辑,不支持富文本;
- write-only: 使元素仅用于编辑(几乎没有浏览器支持)
Range对象
可以用 Document
对象的 Document.createRange
方法创建 Range,也可以用 Selection
对象的 getRangeAt
方法获取 Range。另外,还可以通过 Document
对象的构造函数 Range()
来得到 Range。我在实现的时候通过的是selection对象(表示用户选择的文本范围或插入符号的当前位置)的方法创建的,通过监听selectionchange事件来响应式的更新我的range,这样我就可以定位到光标的位置,那么对于标签插在哪的问题就解决了。
需求:点击插入问题,会在textArea中新增一个tag,并且在textArea中添加了tag后,按钮是处于禁用的状态,当保存后,编辑该prompt时,内容还是会在,所以此处我使用了dangerouslySetInnerHTML。

interface TextareaProps {
defaultValue: string;
getRange?: (range: Range) => void;
ref?: React.Ref<any>;
onChange: (innerHtml: string) => void;
isHaveTag: (isHaveTag: boolean) => void;
}
const TagTextarea: FC<TextareaProps> = forwardRef((props, ref) => {
const [contentId, setContentId] = useState(`content${uuid()}`);
const inputTag = useRef<HTMLDivElement>(null);
useImperativeHandle(
ref,
() => {
return {
addTag,
};
},
[]
);
const handleInput = () => {
const tagTextareaValue = (inputTag as any).current.innerHTML;
props.onChange((inputTag as any).current.innerHTML);
const regex = /id="textarea_tag"/;
props.isHaveTag(regex.test(tagTextareaValue));
};
const selecthandler = () => {
const sel = window.getSelection();
const range = sel ? (sel.rangeCount > 0 ? sel?.getRangeAt(0) : null) : null;
if (range && range.commonAncestorContainer.ownerDocument?.activeElement?.id === contentId) {
props.getRange && props.getRange(range);
}
};
useEffect(() => {
handleInput();
}, []);
useEffect(() => {
document.addEventListener('selectionchange', selecthandler);
return () => {
document.removeEventListener('selectionchange', selecthandler);
};
}, []);
const insertNode = (node: Element, range: Range) => {
range && range.deleteContents();
range && range.insertNode(node);
handleInput();
};
const addTag = (text: string, range: Range) => {
const tag = (
<div id='textarea_tag' className='textarea_tag'>
{text}
</div>
);
const componentString = ReactDOMServer.renderToString(tag);
const componentElement: any = new DOMParser().parseFromString(componentString, 'text/html').body
.firstChild;
insertNode(componentElement, range);
};
return (
<div className='tagTextArea'>
<div
contentEditable='true'
className='overflow-auto myTextArea px-3 py-1.5 outline-none bg-white text-[#303030] text-sm block rounded-md border-0 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6'
onInput={handleInput}
ref={inputTag}
id={contentId}
dangerouslySetInnerHTML={{ __html: props.defaultValue }}
></div>
</div>
);
});
.myTextArea {
-webkit-user-modify: read-write-plaintext-only !important;
border: 1px solid #ccc;
overflow: hidden;
box-sizing: border-box;
word-break: break-word;
height: 60px;
}
.textarea_tag {
color: #c41d7f;
background: #fff0f6;
border-color: #ffadd2;
-webkit-user-modify: read-only;
display: inline;
margin: 0 6px;
font-size: 12px;
padding-inline: 7px;
border: 1px solid #d9d9d9;
border-radius: 4px;
}
网友评论