这篇文章主要介绍“怎么使用react编写可编辑标题”,在日常操作中,相信很多人在怎么使用react编写可编辑标题问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么使用react编写可编辑标题”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
文案支持可编辑
用户点击位置即光标定位处
超过50字读的时候,超出部分进行截断
当用户把所有内容删除时,失去焦点时文案设置为 “无文案”三个字
编辑区域随着编辑内容的宽度而变化,最大宽度1000px 500px
失去焦点时保存文案内容
在看到第一眼需求的时候,想到的时候用span和input进行切换,但是这个肯定是满足不了需求中第2点,所以首先这个需求肯定不会是两个 标签切换,只能一个标签承担展示和编辑的功能,第一反应是用html属性contentEditable,就有了我的第一个套方案,后因为需求的第三点实现上存在问题,所以被迫换了方案二(使用input标签),下面我们详细说说为啥弃用方案1选用方案二以及在这过程中遇到的问题。
利用h6提供contentEditble,可实现需求点的1/2/5
监听focus事件和input时间,可以实现需求点4
监听blur事件,可以实现需求点3
但是 需求点中的3点,因为是用字数做的截断,在这个方案中是实现不了的,所以我给出的建议方案是编辑的时候不做截断,非编辑的时候做截断段(是否失去焦点可用作判断是否为编辑态的依据)
演示demo:
import React, { useState, useRef, useEffect } from 'react';
import ReactDom from 'react-dom';
interface EditTextProps {
text: string;
// 告知父组件文案已被修改
changeText?: (text: string) => void;
}
const EditText = function (props: EditTextProps) {
useEffect(() => {
setShowText(props.text);
}, [props.text]);
const [showText, setShowText] = useState('');
const [isBlank, setIsBlank] = useState(false);
const [isFocus, setIsFocus] = useState(false);
const textRef = useRef<HTMLDivElement>(null);
const onFocus = () => {
setIsFocus(true)
}
const onInput = () => {
// 避免失去焦点的时候,标题区域明显的闪动
setIsBlank(!textRef.current?.innerHTML);
}
const onBlur = () => {
const newTitle = textRef.current?.innerHTML || '无标题';
const oldTitle = props.text;
setIsFocus(false);
setIsBlank(false);
// 文案更新
if (newTitle !== oldTitle) {
props?.changeText(newTitle);
setShowText(getCharsByLength(newTitle, 50));
}
else {
// 文案不更新
setShowText(getCharsByLength(newTitle, 50));
if(textRef.current) {
textRef.current.innerHTML = getCharsByLength(newTitle, 50)
}
}
}
// 获取前length个字符
const getCharsByLength = (title: string, length: number) => {
const titleLength = title.length;
// 假设都是非中文字符,一个中文字符的宽度可以显示两个非中文字符
let maxLength = length * 2;
const result = [];
for (let i = 0; i < titleLength; i++) {
const char = title[i];
// 中文字符宽度2,非中文字符宽度1
maxLength -= /[\u4e00-\u9fa5]/.test(char) ? 2 : 1;
result.push(char);
if (maxLength <= 0) {
break;
}
}
if (result.length < titleLength) {
result.push('...');
}
return result.join('');
};
return <div className="title">
{isFocus && isBlank ? <span className="title-blank">无标题</span> : ''}
<span
className="title-text"
contentEditable
suppressContentEditableWarning
ref={textRef}
onFocus={onFocus}
onInput={onInput}
onBlur={onBlur}
>{showText}</span>
</div>;
};
如果在用户修改之前的文案就是【无标题】,此时用户删除了文案所有的内容【将文案置空】,此时失去焦点,根据需求我们应该展示【无标题】,可是在代码逻辑中 进行了setShowText(getCharsByLength(newTitle, 50));
的处理,在不断试探中,发现修改前后的showText
一摸一样,无法触发dom的更新,针对这个问题我找到了两个解决方式
方式一 在不需要更新标题,用户触发了失去焦点,但是并没有修改标题时,先把showText设置为空,在setTimeout中设置会以前的标题。
尝试了一下这个方案,从使用角度来说并不会特别明显的闪动。不过个人觉得这个方案代码看着很怪异
const onBlur = () => {
const newTitle = textRef.current?.innerHTML || '无标题';
const oldTitle = props.text;
setIsFocus(false);
setIsBlank(false);
// 文案更新
if (newTitle !== oldTitle) {
props?.changeText(newTitle);
setShowText(getCharsByLength(newTitle, 50));
}
else {
// 文案不更新
setShowText('');
setTimeout(() => {
setShowText(getCharsByLength(newTitle, 50));
}, 0)
}
}
方式二 利用ref
const onBlur = () => {
const newTitle = textRef.current?.innerHTML || '无标题';
const oldTitle = props.text;
setIsFocus(false);
setIsBlank(false);
// 文案更新
if (newTitle !== oldTitle) {
props?.changeText(newTitle);
setShowText(getCharsByLength(newTitle, 50));
}
else {
// 文案不更新
setShowText(getCharsByLength(newTitle, 50));
if(textRef.current) {
textRef.current.innerHTML = getCharsByLength(newTitle, 50)
}
}
}
无法用字数做限制
如果用宽度做限制,可以出现截断的效果,但是内容无法滑动
采用修改input框样式的方法,让input展示和可编辑文案。整体的效果和文章开头展示的效果一致。 canEdit
这个参数时我后面加的,用来控制EditText
组件是否可以编辑。遇到的问题见面后面。 演示demo:
import React, { useState, useEffect, useRef, useLayoutEffect } from 'react';
interface EditTextProps {
text: string;
canEdit?: boolean;
changeText?: (text: string) => void;
}
function EditText(props: EditTextProps) {
// 根据span获取宽度
const witdthRef = useRef<HTMLDivElement>(null);
const [showText, setShowText] = useState('');
const [isFocus, setIsFocus] = useState(false);
const [inputWith, setInputWith] = useState(100);
const minTitleWidth = 70;
const maxTitleWidth = 500;
useEffect(() => {
setShowText(props.text);
}, [props.text]);
useLayoutEffect(() => {
dealInputWidth();
}, [showText]);
const dealInputWidth = () => {
const offsetWidth = witdthRef?.current?.offsetWidth || minTitleWidth;
// +5 防止出现 截断
const width = offsetWidth < maxTitleWidth ? offsetWidth + 5 : maxTitleWidth;
setInputWith(width);
};
const titleFocus = () => {
setIsFocus(true);
};
const titleInput = (e: React.ChangeEvent<HTMLInputElement>) => {
const newTitle = e.target.value;
setShowText(newTitle);
};
const titleBlur = () => {
const newTitle = showText || '无标题';
const oldTitle = props.text;
setIsFocus(false);
if (showText !== oldTitle) {
setShowText(newTitle);
setIsFocus(false);
if (props?.changeText) {
props.changeText(newTitle);
}
} else {
setIsFocus(false);
setShowText(newTitle);
}
};
return (
<div className='wrap'>
{props.canEdit ? (
<input
value={showText}
style={{ width: inputWith }}
onFocus={titleFocus}
onChange={titleInput}
onBlur={titleBlur}
className='input'
placeholder="无标题"
/>
) : (
''
)}
{/* 为了计算文字的宽度 */}
<span ref={witdthRef} className={props.canEdit ? 'width' : 'text'}>
{showText}
</span>
</div>
);
}
到此,关于“怎么使用react编写可编辑标题”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。