欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

react編寫(xiě)可編輯標(biāo)題示例詳解

 更新時(shí)間:2022年08月23日 16:59:49   作者:捌玖ki  
這篇文章主要為大家介紹了react編寫(xiě)可編輯標(biāo)題示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

需求

因?yàn)樽约簱Q工作到了新公司,上周入職,以前沒(méi)有使用過(guò)react框架,雖然前面有學(xué)習(xí)過(guò)react,但是并沒(méi)有實(shí)踐經(jīng)驗(yàn)

這個(gè)需求最終的效果是和石墨標(biāo)題修改實(shí)現(xiàn)一樣的效果

初始需求

  • 文案支持可編輯
  • 用戶(hù)點(diǎn)擊位置即光標(biāo)定位處
  • 超過(guò)50字讀的時(shí)候,超出部分進(jìn)行截?cái)?/li>
  • 當(dāng)用戶(hù)把所有內(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è)肯定是滿(mǎn)足不了需求中第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

思路

  • 利用h5提供contentEditble,可實(shí)現(xiàn)需求點(diǎn)的1/2/5
  • 監(jiān)聽(tīng)focus事件和input時(shí)間,可以實(shí)現(xiàn)需求點(diǎn)4
  • 監(jiān)聽(tīng)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)啵蔷庉嫷臅r(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)題

如果在用戶(hù)修改之前的文案就是【無(wú)標(biāo)題】,此時(shí)用戶(hù)刪除了文案所有的內(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)題,用戶(hù)觸發(fā)了失去焦點(diǎn),但是并沒(méi)有修改標(biāo)題時(shí),先把showText設(shè)置為空,在setTimeout中設(shè)置會(huì)以前的標(biāo)題。

嘗試了一下這個(gè)方案,從使用角度來(lái)說(shuō)并不會(huì)特別明顯的閃動(dòng)。不過(guò)個(gè)人覺(jué)得這個(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展示和可編輯文案。整體的效果和文章開(kāi)頭展示的效果一致。 canEdit這個(gè)參數(shù)時(shí)我后面加的,用來(lái)控制EditText組件是否可以編輯。遇到的問(wèn)題見(jià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>
  );
}  

踩到的坑

input自帶寬度,無(wú)法實(shí)現(xiàn)寬度隨著文案的改變而改變。

在方案一做出來(lái)后,就和UI進(jìn)行了溝通在【編輯的時(shí)候用字?jǐn)?shù)做截?cái)鄬?shí)現(xiàn)不了】,給出了一個(gè)建議的方案【編輯的時(shí)候不做截?cái)唷?,但是設(shè)計(jì)同學(xué)覺(jué)得不截?cái)嗟姆桨高^(guò)丑,,,,,然后她就說(shuō)能實(shí)現(xiàn) 【石墨標(biāo)題編輯】時(shí),類(lèi)似的效果交互嗎???于是我就開(kāi)啟了研究石墨的效果的征途中。

只發(fā)現(xiàn) 石墨用了一個(gè)input實(shí)現(xiàn)了不錯(cuò)的效果,input后面放了一個(gè)span標(biāo)簽,我體驗(yàn)的時(shí)候,一直在想為什么會(huì)有一個(gè)span標(biāo)簽?zāi)兀??(小朋友,是不是滿(mǎn)臉疑問(wèn))

直到我發(fā)現(xiàn)input自帶寬度,無(wú)法隨著內(nèi)容的寬度的改變而改變。此時(shí)才恍然大悟span標(biāo)簽的作用。

我也采用了利用span標(biāo)簽的寬度的方式來(lái)控input輸入內(nèi)容的寬度。
開(kāi)玩笑,咋可能這么順利,我遇到了第二個(gè)問(wèn)題

用useEffect 來(lái)監(jiān)控 witdthRef.current.offsetWidth時(shí),拿到的是上次文案的寬度 經(jīng)過(guò)查閱資料,我發(fā)現(xiàn)了useLayoutEffect這個(gè)hook,真香

以上就是react編寫(xiě)可編輯標(biāo)題示例詳解的詳細(xì)內(nèi)容,更多關(guān)于react編寫(xiě)可編輯標(biāo)題的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • React Hooks 實(shí)現(xiàn)和由來(lái)以及解決的問(wèn)題詳解

    React Hooks 實(shí)現(xiàn)和由來(lái)以及解決的問(wèn)題詳解

    這篇文章主要介紹了React Hooks 實(shí)現(xiàn)和由來(lái)以及解決的問(wèn)題詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • 詳解React 和 Redux的關(guān)系

    詳解React 和 Redux的關(guān)系

    這篇文章主要為大家介紹了React 和 Redux的關(guān)系,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2021-11-11
  • 基于React Native 0.52實(shí)現(xiàn)輪播圖效果

    基于React Native 0.52實(shí)現(xiàn)輪播圖效果

    這篇文章主要為大家詳細(xì)介紹了基于React Native 0.52實(shí)現(xiàn)輪播圖效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • Header組件熱門(mén)搜索欄的實(shí)現(xiàn)示例

    Header組件熱門(mén)搜索欄的實(shí)現(xiàn)示例

    這篇文章主要為大家介紹了Header組件熱門(mén)搜索欄的實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • React高階組件使用教程詳解

    React高階組件使用教程詳解

    高階組件就是接受一個(gè)組件作為參數(shù)并返回一個(gè)新組件(功能增強(qiáng)的組件)的函數(shù)。這里需要注意高階組件是一個(gè)函數(shù),并不是組件,這一點(diǎn)一定要注意,本文給大家分享React 高階組件HOC使用小結(jié),一起看看吧
    2022-12-12
  • React Native全面屏狀態(tài)欄和底部導(dǎo)航欄適配教程詳細(xì)講解

    React Native全面屏狀態(tài)欄和底部導(dǎo)航欄適配教程詳細(xì)講解

    最近在寫(xiě) React Native 項(xiàng)目,調(diào)試應(yīng)用時(shí)發(fā)現(xiàn)頂部狀態(tài)欄和底部全面屏手勢(shì)指示條區(qū)域不是透明的,看起來(lái)很難受。研究了一下這個(gè)問(wèn)題,現(xiàn)在總結(jié)一下解決方案,這篇文章主要介紹了React Native全面屏狀態(tài)欄和底部導(dǎo)航欄適配教程
    2023-01-01
  • 使用 React 和 Threejs 創(chuàng)建一個(gè)VR全景項(xiàng)目的過(guò)程詳解

    使用 React 和 Threejs 創(chuàng)建一個(gè)VR全景項(xiàng)目的過(guò)程詳解

    這篇文章主要介紹了使用 React 和 Threejs 創(chuàng)建一個(gè)VR全景項(xiàng)目的過(guò)程詳解,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • Parcel 打包示例(React HelloWorld)

    Parcel 打包示例(React HelloWorld)

    本篇文章主要介紹了Parcel 打包示例(React HelloWorld),詳細(xì)的介紹了Parcel打包的特點(diǎn)和使用示例,有興趣的可以了解一下
    2018-01-01
  • React中的頁(yè)面跳轉(zhuǎn)方式示例詳解

    React中的頁(yè)面跳轉(zhuǎn)方式示例詳解

    React Router提供了幾種不同的跳轉(zhuǎn)方式,包括使用組件進(jìn)行頁(yè)面跳轉(zhuǎn)、使用組件進(jìn)行重定向,以及使用編程式導(dǎo)航進(jìn)行跳轉(zhuǎn),這篇文章主要介紹了React中的頁(yè)面跳轉(zhuǎn)方式詳解,需要的朋友可以參考下
    2023-09-09
  • React Router 中實(shí)現(xiàn)嵌套路由和動(dòng)態(tài)路由的示例

    React Router 中實(shí)現(xiàn)嵌套路由和動(dòng)態(tài)路由的示例

    React Router 是一個(gè)非常強(qiáng)大和靈活的路由庫(kù),它為 React 應(yīng)用程序提供了豐富的導(dǎo)航和 URL 管理功能,能夠幫助我們構(gòu)建復(fù)雜的單頁(yè)應(yīng)用和多頁(yè)應(yīng)用,這篇文章主要介紹了React Router 中如何實(shí)現(xiàn)嵌套路由和動(dòng)態(tài)路由,需要的朋友可以參考下
    2023-05-05

最新評(píng)論