这是抖音的发布页面,此篇文章目的就是为了模仿input输入特定的文本为标签,并且显示不同的样式,
本组件使用一个 div
元素作为输入框,通过 CSS 样式和 JavaScript 动态操作 DOM,来实现与传统 input
输入框相似的功能。用户在输入框中输入以 #
开头的文本时,组件会将其解析为标签,并使用不同的样式进行显示。
import React, { useState, FC, useRef } from 'react';
interface InputTagProps {
value: string;
onChange: (value: string) => void;
}
const InputTag: FC<InputTagProps> = ({ value, onChange }) => {
const myRef = useRef<HTMLDivElement>(null);
const [isEditing, setIsEditing] = useState(false);
interface ParsedText {
type: string;
content: string;
}
const parseText = (input: string): ParsedText[] => {
// 使用新的正则表达式来分割文本,保留空格
const parts = input.split(/((?<=\s)|(?=\s)|(?=#))/g).filter(Boolean);
return parts.reduce<ParsedText[]>((acc, part) => {
if (part.trim() === '') {
// 保留空格
acc.push({ type: 'space', content: part });
} else if (part.startsWith('#')) {
// 修改标签的正则表达式,允许单个#
if (/^#[\w\u4e00-\u9fa5]*$/.test(part)) {
acc.push({ type: 'tags', content: part });
} else {
// 如果不是有效的标签,作为普通文本处理
acc.push({ type: 'text', content: part });
}
} else {
acc.push({ type: 'text', content: part });
}
return acc;
}, []);
};
const handleClick = () => {
setIsEditing(true);
if (myRef.current) {
myRef.current.innerText = ''; // 清空输入框内容
}
};
const handleBlur = () => {
setIsEditing(false);
if (myRef.current) {
const innerText = myRef.current.innerText;
onChange(innerText); // 更新外部值
myRef.current.innerHTML = ''; // 清空现有内容
// 根据解析的文本动态创建子元素
parseText(innerText).forEach((item) => {
const span = document.createElement('span');
span.innerText = item.content;
span.style.color = item.type === 'tags' ? 'green' : 'black';
myRef.current.appendChild(span);
});
}
};
const handleInput = (event: React.FormEvent<HTMLDivElement>) => {
console.log("chu")
if (myRef.current) {
const innerText = myRef.current.innerText;
// 清空内容以便重新填充
myRef.current.innerHTML = '';
// 根据解析的文本动态创建子元素
parseText(innerText).forEach((item) => {
const span = document.createElement('span');
span.innerText = item.content;
span.style.color = item.type === 'tags' ? 'green' : 'black';
myRef.current.appendChild(span);
});
// 保持光标在最后
const range = document.createRange();
const selection = window.getSelection();
range.selectNodeContents(myRef.current);
range.collapse(false); // 将光标放在最后
selection?.removeAllRanges();
selection?.addRange(range);
}
};
return (
<div
ref={myRef}
style={{
border: '1px solid #ccc',
borderRadius: '4px',
padding: '8px',
width: '100%',
cursor: isEditing ? 'text' : 'pointer',
}}
onClick={handleClick}
onInput={handleInput}
onBlur={handleBlur}
contentEditable="true"
data-placeholder="请输入内容"
data-node="true"
>
</div>
);
};
export default InputTag;
-
动态解析输入文本:用户输入文本后,组件会实时解析输入的内容,并根据规则将特定格式的文本(如以
#
开头的标签)动态生成对应的span
标签。 -
自定义样式:标签的样式可根据其类型进行不同的设置,例如使用绿色显示标签,黑色显示普通文本。
-
支持中文标签:通过修改正则表达式,组件支持中文字符作为标签的一部分,使其更加灵活和适应多语言环境。
在实现过程中,遇到了一些问题,例如无法实时处理输入、输入 #
时内容被清空等。通过不断调试和测试,最终成功解决了这些问题。这一过程提醒我们,在开发中,细致入微的测试是必不可少的。通过对代码的逐行检查和调试,我们能够发现潜在的错误和性能问题。
感谢您阅读这篇文章。如果你对该组件有任何疑问或建议,请随时联系我!希望能够与你共同探讨更多前端开发的最佳实践。
4o