如何给TextArea文本框中,点击按钮新增一个Tag标签

2023-06-20  本文已影响0人  Poppy11

contenteditable

修改元素的可编辑性,使之可以编辑,html就给我们提供了这么个属性,要知道在一个普通元素内插入一个标签要比在textarea中要容易多了。

-webkit-user-modify

Range对象

可以用 Document 对象的 Document.createRange 方法创建 Range,也可以用 Selection 对象的 getRangeAt 方法获取 Range。另外,还可以通过 Document 对象的构造函数 Range() 来得到 Range。我在实现的时候通过的是selection对象(表示用户选择的文本范围或插入符号的当前位置)的方法创建的,通过监听selectionchange事件来响应式的更新我的range,这样我就可以定位到光标的位置,那么对于标签插在哪的问题就解决了。

需求:点击插入问题,会在textArea中新增一个tag,并且在textArea中添加了tag后,按钮是处于禁用的状态,当保存后,编辑该prompt时,内容还是会在,所以此处我使用了dangerouslySetInnerHTML。

image.png

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;
}

上一篇 下一篇

猜你喜欢

热点阅读