温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

怎么使用react编写可编辑标题

发布时间:2022-08-24 10:50:07 来源:亿速云 阅读:108 作者:iii 栏目:开发技术

这篇文章主要介绍“怎么使用react编写可编辑标题”,在日常操作中,相信很多人在怎么使用react编写可编辑标题问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么使用react编写可编辑标题”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

    需求

    初始需求

    • 文案支持可编辑

    • 用户点击位置即光标定位处

    • 超过50字读的时候,超出部分进行截断

    • 当用户把所有内容删除时,失去焦点时文案设置为 “无文案”三个字

    • 编辑区域随着编辑内容的宽度而变化,最大宽度1000px 500px

    • 失去焦点时保存文案内容

    方案设计

    在看到第一眼需求的时候,想到的时候用span和input进行切换,但是这个肯定是满足不了需求中第2点,所以首先这个需求肯定不会是两个 标签切换,只能一个标签承担展示和编辑的功能,第一反应是用html属性contentEditable,就有了我的第一个套方案,后因为需求的第三点实现上存在问题,所以被迫换了方案二(使用input标签),下面我们详细说说为啥弃用方案1选用方案二以及在这过程中遇到的问题。

    方案一 span + contentEditable

    思路
    • 利用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框样式的方法,让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编写可编辑标题”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    AI