溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么使用react編寫可編輯標(biāo)題

發(fā)布時(shí)間:2022-08-24 10:50:07 來(lái)源:億速云 閱讀:111 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹“怎么使用react編寫可編輯標(biāo)題”,在日常操作中,相信很多人在怎么使用react編寫可編輯標(biāo)題問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”怎么使用react編寫可編輯標(biāo)題”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

    需求

    初始需求

    • 文案支持可編輯

    • 用戶點(diǎn)擊位置即光標(biāo)定位處

    • 超過(guò)50字讀的時(shí)候,超出部分進(jìn)行截?cái)?/p>

    • 當(dāng)用戶把所有內(nèi)容刪除時(shí),失去焦點(diǎn)時(shí)文案設(shè)置為 “無(wú)文案”三個(gè)字

    • 編輯區(qū)域隨著編輯內(nèi)容的寬度而變化,最大寬度1000px 500px

    • 失去焦點(diǎn)時(shí)保存文案內(nèi)容

    方案設(shè)計(jì)

    在看到第一眼需求的時(shí)候,想到的時(shí)候用span和input進(jìn)行切換,但是這個(gè)肯定是滿足不了需求中第2點(diǎn),所以首先這個(gè)需求肯定不會(huì)是兩個(gè) 標(biāo)簽切換,只能一個(gè)標(biāo)簽承擔(dān)展示和編輯的功能,第一反應(yīng)是用html屬性contentEditable,就有了我的第一個(gè)套方案,后因?yàn)樾枨蟮牡谌c(diǎn)實(shí)現(xiàn)上存在問(wèn)題,所以被迫換了方案二(使用input標(biāo)簽),下面我們?cè)敿?xì)說(shuō)說(shuō)為啥棄用方案1選用方案二以及在這過(guò)程中遇到的問(wèn)題。

    方案一 span + contentEditable

    思路
    • 利用h6提供contentEditble,可實(shí)現(xiàn)需求點(diǎn)的1/2/5

    • 監(jiān)聽focus事件和input時(shí)間,可以實(shí)現(xiàn)需求點(diǎn)4

    • 監(jiān)聽blur事件,可以實(shí)現(xiàn)需求點(diǎn)3

    但是 需求點(diǎn)中的3點(diǎn),因?yàn)槭怯米謹(jǐn)?shù)做的截?cái)?,在這個(gè)方案中是實(shí)現(xiàn)不了的,所以我給出的建議方案是編輯的時(shí)候不做截?cái)?,非編輯的時(shí)候做截?cái)喽危ㄊ欠袷ソ裹c(diǎn)可用作判斷是否為編輯態(tài)的依據(jù))

    代碼如下

    演示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 = () => {
        // 避免失去焦點(diǎn)的時(shí)候,標(biāo)題區(qū)域明顯的閃動(dòng)
        setIsBlank(!textRef.current?.innerHTML);
      }
      const onBlur = () => {
        const newTitle = textRef.current?.innerHTML || '無(wú)標(biāo)題';
        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)
          }
        }
      }
      // 獲取前l(fā)ength個(gè)字符
      const getCharsByLength = (title: string, length: number) => {
        const titleLength = title.length;
        // 假設(shè)都是非中文字符,一個(gè)中文字符的寬度可以顯示兩個(gè)非中文字符
        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">無(wú)標(biāo)題</span> : ''}
        <span
          className="title-text"
          contentEditable
          suppressContentEditableWarning
          ref={textRef}
          onFocus={onFocus}
          onInput={onInput}
          onBlur={onBlur}
        >{showText}</span>
      </div>;
    };
    在這個(gè)方案中遇到的問(wèn)題

    如果在用戶修改之前的文案就是【無(wú)標(biāo)題】,此時(shí)用戶刪除了文案所有的內(nèi)容【將文案置空】,此時(shí)失去焦點(diǎn),根據(jù)需求我們應(yīng)該展示【無(wú)標(biāo)題】,可是在代碼邏輯中 進(jìn)行了setShowText(getCharsByLength(newTitle, 50));的處理,在不斷試探中,發(fā)現(xiàn)修改前后的showText一摸一樣,無(wú)法觸發(fā)dom的更新,針對(duì)這個(gè)問(wèn)題我找到了兩個(gè)解決方式

    • 方式一 在不需要更新標(biāo)題,用戶觸發(fā)了失去焦點(diǎn),但是并沒有修改標(biāo)題時(shí),先把showText設(shè)置為空,在setTimeout中設(shè)置會(huì)以前的標(biāo)題。

    嘗試了一下這個(gè)方案,從使用角度來(lái)說(shuō)并不會(huì)特別明顯的閃動(dòng)。不過(guò)個(gè)人覺得這個(gè)方案代碼看著很怪異

    const onBlur = () => {
        const newTitle = textRef.current?.innerHTML || '無(wú)標(biāo)題';
        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 || '無(wú)標(biāo)題';
        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)
          }
        }
      }
    存在的問(wèn)題
    • 無(wú)法用字?jǐn)?shù)做限制

    • 如果用寬度做限制,可以出現(xiàn)截?cái)嗟男Ч?,但是?nèi)容無(wú)法滑動(dòng)

    方案二 直接用input處理展示和編輯

    采用修改input框樣式的方法,讓input展示和可編輯文案。整體的效果和文章開頭展示的效果一致。 canEdit這個(gè)參數(shù)時(shí)我后面加的,用來(lái)控制EditText組件是否可以編輯。遇到的問(wèn)題見面后面。 演示demo:

    import React, { useState, useEffect, useRef, useLayoutEffect } from 'react';
    interface EditTextProps {
      text: string;
      canEdit?: boolean;
      changeText?: (text: string) => void;
    }
    function EditText(props: EditTextProps) {
      // 根據(jù)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 防止出現(xiàn) 截?cái)?
        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 || '無(wú)標(biāo)題';
        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="無(wú)標(biāo)題"
              />
            ) : (
              ''
            )}
            {/* 為了計(jì)算文字的寬度 */}
            <span ref={witdthRef} className={props.canEdit ? 'width' : 'text'}>
              {showText}
            </span>
          </div>
      );
    }

    到此,關(guān)于“怎么使用react編寫可編輯標(biāo)題”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI