这篇文章主要介绍“怎么使用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编写可编辑标题”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。