最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用JavaScript實(shí)現(xiàn)文本收起展開(kāi)(省略)功能

 更新時(shí)間:2024年04月02日 09:16:26   作者:貓頭_  
省略號(hào),作為一種常見(jiàn)的文本處理方式,在很多情況下都十分常見(jiàn),特別是當(dāng)我們需要在省略號(hào)后面添加額外文字時(shí),本文為大家介紹了使用JavaScript實(shí)現(xiàn)文本收起展開(kāi)功能的相關(guān)方法,希望對(duì)大家有所幫助

省略號(hào),作為一種常見(jiàn)的文本處理方式,在很多情況下都十分常見(jiàn)。特別是當(dāng)我們需要在省略號(hào)后面添加額外文字時(shí),這種需求更是不少見(jiàn)。

然而,僅僅依賴 CSS 來(lái)實(shí)現(xiàn)兼容性、流暢的文本省略效果是不現(xiàn)實(shí)的。就拿 Vant4TextEllipsis 文本省略功能來(lái)說(shuō),我們可以從中學(xué)到不少。

參考Vant4:TextEllipsis 文本省略,為大家分析一波案例(感覺(jué)麻煩可以去看下面的附帶代碼)。

在整個(gè)代碼中,關(guān)鍵之處在于使用了 tail、middleTail 等關(guān)鍵手段。我們著重討論 tail,即在文本尾部插入內(nèi)容的操作。

簡(jiǎn)單的CSS隱藏

大家或多或少都曾編寫(xiě)過(guò) CSS 省略文本的代碼。比如,要將文本限制在四行內(nèi)顯示,可以使用以下 CSS 代碼:

隱藏為四行

overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 4; /* 控制顯示行數(shù) */

其中的限制

盡管如此,這種方法有著一定的局限性:

  • 如何在省略號(hào)后添加展開(kāi)、收起等文字?
  • 添加后,如何保證文字與省略號(hào)在同一行?
  • 展開(kāi)后,如何保證文字在合適位置顯示?

純 CSS 可以實(shí)現(xiàn)這些效果,但實(shí)際操作起來(lái)會(huì)比較困難。

既然 CSS 不夠理想,我們可以考慮使用 JavaScript 來(lái)實(shí)現(xiàn)這些功能。

首先,讓我們來(lái)分析一波:

  • 收起時(shí)保留的高度是多少?
  • 如何截取超出高度的文字內(nèi)容?
  • 如何確保截取后的文字內(nèi)容不影響原來(lái)的渲染和展示?

完成以上三點(diǎn),我們就完成了任務(wù)。

開(kāi)始分析

<div class="root">
    散文是一種文學(xué)形式,與之相對(duì)的稱為韻文或詩(shī)文。散文可分為廣義與狹義的解釋,其中,廣義散文最容易辨識(shí)與定義的方式,是“松散”的結(jié)構(gòu)。也就是說(shuō),扣除其它文學(xué)形式重疊部分,運(yùn)用普通語(yǔ)法結(jié)構(gòu),不講究音韻,不講究排比,沒(méi)有任何束縛及限制的文字梳理方式,都可稱為散文。除此,狹義散文是單指文學(xué)范疇內(nèi),結(jié)構(gòu)松散之非韻文作品。文學(xué)專指的散文,歷代作品有著各時(shí)代不同流變的脈絡(luò),而正因?yàn)樗缮?lái)的自由,散文作品表達(dá)出的思想通常有著豐富與圓滿的特色
</div>
<style>
  .root {
    line-height: 1.6;
    overflow-wrap: break-word;
    word-break: break-all;
  }
</style>

首先解決:收起時(shí)要保留的高度是多少?

假設(shè)我們保留4行:

需要給予文本限高 : line-height: 1.6;

row = 4
const maxHeight = Math.ceil(
    (Number(rows) + 0.5) * pxToNum(lineHeight) + "一些其他的容器高度:比如padding"
);

在這個(gè)計(jì)算中,0.5 是一個(gè)調(diào)整值,用于在計(jì)算最大高度時(shí)考慮到文本行數(shù)的不確定性。這個(gè)調(diào)整值的目的是為了確保即使行數(shù)計(jì)算稍微超過(guò)了預(yù)期的行數(shù),也能夠提前截?cái)辔谋?,以防止文本超出容器的高度限制?/p>

得到了高度,如何切割文字

注意看:tail() 函數(shù)是一個(gè)遞歸函數(shù),用于在給定的容器高度限制下,計(jì)算文本的截?cái)辔恢?。讓我?lái)詳細(xì)解釋一下 tail() 函數(shù)的工作原理:

const tail = (left, right) => {

    if (right - left <= 1) {
      return content.slice(0, left) + dots;
    }
    
    const middle = Math.round((left + right) / 2);
    container.innerText = content.slice(0, middle) + dots + actionText;
    
    if (container.offsetHeight > maxHeight) {
      return tail(left, middle);
    }
    return tail(middle, right);
  };

  container.innerText = tail(0, end);

當(dāng)遞歸結(jié)束時(shí),函數(shù)會(huì)返回最終的截?cái)辔恢?,即最佳的截?cái)辔恢谩?/p>

遞歸都有自己結(jié)束判斷,這個(gè)遞歸的結(jié)束點(diǎn)就在right - left <= 1,

計(jì)算中間位置 middle ,并將文本內(nèi)容從左邊界到中間位置加上省略符號(hào),然后設(shè)置到容器元素中

檢查容器元素的高度是否超過(guò)了指定的最大高度限制。如果超過(guò)了,說(shuō)明當(dāng)前截?cái)辔恢眠^(guò)早,需要繼續(xù)向左邊界靠近進(jìn)行截?cái)啵环駝t,說(shuō)明當(dāng)前截?cái)辔恢眠^(guò)晚,需要繼續(xù)向右邊界靠近進(jìn)行截?cái)唷?/p>

最終找到,合適的middle值,在一起返回 content.slice(0, left) + dots 作為結(jié)點(diǎn)的 innerText 文字。

期間,一直在收縮,就算高度相同也在收縮右邊界的內(nèi)容, right - left <= 1 的作用就是剛剛好實(shí)現(xiàn)的截?cái)辔恢靡詽M足給定的最大高度限制,不會(huì)超出限制。

總結(jié):這個(gè) tail() 函數(shù)使用遞歸的方式,根據(jù)給定的容器高度限制,計(jì)算出文本的截?cái)辔恢?。遞歸的結(jié)束判斷在 right - left <= 1,通過(guò)不斷調(diào)整 middle 的值,最終找到合適的截?cái)辔恢靡詽M足給定的最大高度限制,不會(huì)超出限制。

在切割下,保證文字內(nèi)容不影響原來(lái)的渲染展示

為了不影響原文本的渲染展示,我們使用了克隆節(jié)點(diǎn)的方式。這里參考了 Vant 中的代碼,并做了適當(dāng)修改。

修改成了,更加適合JS寶寶體質(zhì)的代碼。

const rootNode = document.querySelector(".root");

const cloneContainer = () => {
  // cloneContainer就是為了得到文本的高度
  if (!rootNode || !rootNode.isConnected) return;

  const originStyle = window.getComputedStyle(rootNode);
  const container = document.createElement('div');
  const styleNames= Array.prototype.slice.apply(originStyle);

  styleNames.forEach((name) => {
    container.style.setProperty(name, originStyle.getPropertyValue(name));
  });

  container.style.position = 'fixed'; // 不在文檔流中
  container.style.zIndex = '-9999'; // 看不到
  container.style.top = '-9999px'; // 看不到
  container.style.height = 'auto';
  container.style.minHeight = 'auto';
  container.style.maxHeight = 'auto';

  container.innerText = content;
  document.body.appendChild(container);

  return container;
};

解析:window.getComputedStyle 是一個(gè)用于獲取指定元素的所有計(jì)算樣式的方法。它返回一個(gè) CSSStyleDeclaration 對(duì)象,其中包含了指定元素的所有計(jì)算樣式屬性及其對(duì)應(yīng)的值。

實(shí)現(xiàn)效果

實(shí)例代碼

html

<style>
  .root {
    padding: 12px;
    line-height: 1.6;
    overflow-wrap: break-word;
    word-break: break-all;
  }
</style>

<body>
  <div class="root">
    散文是一種文學(xué)形式,與之相對(duì)的稱為韻文或詩(shī)文。散文可分為廣義與狹義的解釋,其中,廣義散文最容易辨識(shí)與定義的方式,是“松散”的結(jié)構(gòu)。也就是說(shuō),扣除其它文學(xué)形式重疊部分,運(yùn)用普通語(yǔ)法結(jié)構(gòu),不講究音韻,不講究排比,沒(méi)有任何束縛及限制的文字梳理方式,都可稱為散文。除此,狹義散文是單指文學(xué)范疇內(nèi),結(jié)構(gòu)松散之非韻文作品。文學(xué)專指的散文,歷代作品有著各時(shí)代不同流變的脈絡(luò),而正因?yàn)樗缮?lái)的自由,散文作品表達(dá)出的思想通常有著豐富與圓滿的特色
  </div>
</body>
<script src="TextEllipsis.js"> </script>

JS代碼

document.addEventListener("DOMContentLoaded", function() {
  
let expanded =true;
let collapseText = "收起";
let expandText = "展開(kāi)";
const rows = 3;
let actionText = expanded ? collapseText : expandText;
let hasAction = false;
const rootNode = document.querySelector(".root");
const content = rootNode.innerText;
let text = "";
const dots = "...";

const pxToNum = (value) => {
  if (!value) return 0;
  const match = value.match(/^\d*(\.\d*)?/);
  return match ? Number(match[0]) : 0;
};

const cloneContainer = () => {
  // cloneContainer就是為了得到文本的高度
  if (!rootNode || !rootNode.isConnected) return;

  const originStyle = window.getComputedStyle(rootNode);
  const container = document.createElement('div');
  const styleNames= Array.prototype.slice.apply(originStyle);

  styleNames.forEach((name) => {
    container.style.setProperty(name, originStyle.getPropertyValue(name));
  });

  container.style.position = 'fixed';
  container.style.zIndex = '-9999';
  container.style.top = '-9999px';
  container.style.height = 'auto';
  container.style.minHeight = 'auto';
  container.style.maxHeight = 'auto';

  container.innerText = content;
  document.body.appendChild(container);

  return container;
};

const calcEllipsised = () => {
  const calcEllipsisText = (
    container,
    maxHeight
  ) => {
    const end = content.length;

    const calcEllipse = () => {
      const tail = (left, right) => {

        if (right - left <= 1) {
          return content.slice(0, left) + dots;
        }

        const middle = Math.round((left + right) / 2);

        container.innerText = content.slice(0, middle) + dots + actionText;

        if (container.offsetHeight > maxHeight) {
          return tail(left, middle);
        }

        return tail(middle, right);
      };

      container.innerText = tail(0, end);
    };

    calcEllipse();

    return container.innerText;
  };

  const container = cloneContainer();

  if (!container) {
    needRecalculate = true;
    return;
  }

  let { paddingBottom, paddingTop, lineHeight } = container.style;

  const maxHeight = Math.ceil(
    (Number(rows) + 0.5) * pxToNum(lineHeight) +
      pxToNum(paddingTop) +
      pxToNum(paddingBottom),
  );

  if (maxHeight < container.offsetHeight) {
    hasAction = true;
    text = calcEllipsisText(container, maxHeight);
  } else {
    hasAction = false;
    text = content;
  }

  document.body.removeChild(container);
};

const toggle = (isExpanded = !expanded) => {
  expanded = isExpanded;
  actionText = expanded ? collapseText : expandText;
};


const renderAction = () => {
  calcEllipsised();
  const container = document.createElement('span');
  container.classList.add("expend_txt");
  container.addEventListener('click',()=>{
    toggle();
    container.innerText = hasAction ? actionText : null;
    rootNode.innerText = expanded ? content : text;
    rootNode.appendChild(container);
  });
  container.innerText = hasAction ? actionText : null;;
  rootNode.appendChild(container);
}

renderAction()

})

以上就是使用JavaScript實(shí)現(xiàn)文本收起展開(kāi)(省略)功能的詳細(xì)內(nèi)容,更多關(guān)于JavaScript文本展開(kāi)收起的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

建平县| 科尔| 富源县| 年辖:市辖区| 平山县| 响水县| 乃东县| 曲水县| 文水县| 都兰县| 崇州市| 滕州市| 海林市| 海阳市| 肇州县| 沅江市| 青川县| 全椒县| 伊春市| 通渭县| 墨玉县| 石屏县| 佛山市| 襄垣县| 沂水县| 山阴县| 东山县| 罗源县| 兰州市| 合山市| 元谋县| 库尔勒市| 洛扎县| 红桥区| 莲花县| 连州市| 富阳市| 漯河市| 勐海县| 陕西省| 罗江县|