用JS解決多行溢出文本的省略問題

前言

在項目開發過程當中,常常會遇到溢出文本的省略問題。根據需求,能夠把文本省略分爲單行文本省略和多行文本省略兩類。javascript

單行文本的省略,如今css樣式 text-overflow 已經有兼容性很好的樣式支持了。可是多行文本,目前支持webkit內核的css樣式 -webkit-line-clamp 能夠作到,但它針對火狐瀏覽器就行不通了。這就是本文要解決的問題。css

css解決方案(可跳過)

若是上網搜索【多行文本省略】,除了上文提到的css樣式控制,找到的答案都是作固定位置的遮蓋,並且能發現的是,目前幾乎全部的博客都是這種方法(這大概是如今互聯網技術博客的一個通病了,作原創的真的不多)。下面是這些方法的具體代碼:html

方案一:webkit專屬樣式法java

div { /* 對容器自己作限制 */
  display: -webkit-box;     /* 彈性盒舊式語法 */
  -webkit-box-orient: vertical;    /* 通過我的實測,vertical或horizontal都沒問題,只是必須設置該屬性 */
  -webkit-line-clamp: 2;    /* 限制兩行 */
  overflow: hidden;
}
複製代碼

方案二:css遮蓋法node

p { 
  position: relative;
  height: 40px;
  line-height: 20px;       /* 限制兩行 */
  overflow: hidden;
}
p:after {  /* 對文本標籤自己作限制 */
  content: "...";
  position: absolute;
  bottom: 0;
  right: 0;
  background-color: #fff;
}
複製代碼

這種方法只能說想固然地解決問題,實際上不少時候它的效果並不理想——有時候最後的文字會「猶抱琵琶半遮面」,有時候自己文字還不夠長時就出現了,如圖:git

這裏是以上代碼的一個演示:css解決多行文本省略演示 代碼演示中case1沒法體現效果。一樣的代碼,能夠去張鑫旭大神的演示空間查看效果:-webkit-line-clamp下多行文字溢出點點點...顯示實例頁面程序員

JS解決方案

當嘗試用css解決不通時,就應該想到用js來解決。我找了不少網上的解決方案,有提到用插件的,也有提到上文css,但真的不多有提到用js的……並且這些解決方案的特色,上文也提到了,「天下文章一大抄」,每每還不提出原博客出處。github

這裏提一下插件解決方案,主要是兩個。一個是基於jQuery的dotdotdot,文檔也很詳細,可是license收費呀/(ㄒoㄒ)/~~;另外一個是Clamp.js,純js插件,雖然免費可是沒什麼文檔,並且做者也好幾年沒更新了(這種坑就會不少了)……綜上,網上的插件都不符合個人心理預期。web

好了,回到js解決方案,既然靠別人不行,那就靠本身吧。可是,用js實現文本省略最大的難點就是:如何知道哪段文本是溢出的?或者說怎麼找到溢出文本並去掉?瀏覽器

原理探究

這裏原理很簡單,就是逐個增長文本字數,比較當時字數時文本的長度是否超過容器的長度。用jQuery來寫也很簡單,可是須要一些css樣式作輔助,不然你根本達不到想要的效果。代碼貼在下面:

#container {
  border: 1px solid;
  height: 90px; 
  width: 30px; // height 和 width 都必要
  /*overflow: hidden;*/
  overflow-wrap: break-word; // 設置文本溢出容器寬度時換行(但會溢出容器長度)
}
#content {
  line-height: 30px; // 必要,控制文本行數。不然文本會被擠壓,行數不定
}
複製代碼
// jQuery 演示
var $container = $('#container')
var $content = $('#container .content')
var str = $content.text()
if ($content.height() > $container.height()) {
  for (var i = 0; i <= str.length; i++) {
    $content.text(str.slice(0,i))
    if ($content.height() > $container.height()) {
      $content.text(str.slice(0,i-2) + "..");
      break;
    }
  }
}
//===================================================================
// 原生 JS 演示
var container = document.getElementById("container");
var content = container.firstElementChild;
var str = content.firstChild.nodeValue;
if (content.scrollHeight > container.offsetHeight) {
  for (var i = 0; i <= str.length; i++) {
    content.firstChild.nodeValue = str.slice(0,i);
    //console.log(content.scrollHeight + ":" + container.offsetHeight);
    if (content.scrollHeight > container.offsetHeight) {
      content.firstChild.nodeValue = str.slice(0,i-2) + "..";
      break;
    }
  }
}
複製代碼

能夠看出,原理是很簡單的,重點是熟悉一些基礎知識。這裏貼上以上代碼的演示:js解決多行文本省略的演示

接着還有什麼?

若是隻是想要解決問題代碼的同窗,看到這裏就ok了;若是想要再看我囉嗦一些東西,能夠耐着性子繼續看下去。

剛剛原生 JS 爲何不用 height?

若是有同窗仔細比對兩段代碼,必定會有上面那個疑惑。事實上,DOM並無提供height屬性。

可是,scrollHightoffsetHeight 又有什麼區別呢?請看下圖:

上圖中,紅框的高度大體體現了 scrollHeight 的高度。假如對外框設置 overflow: hidden,紅框會收縮至黑框內部,可是實際的 scrollHeight 仍是原紅框的高度。

offsetHeightclientHeight 差很少,都是反映元素表現的高度。這三個更詳細的區別請看這個文檔:JavaScrip 教程 / DOM / Element 節點

PS:通過本人實測,jQuery的 .height() 方法反映的是 scrollHeight,本場景是足以應付的,可是一些特殊狀況仍是須要用到DOM提供的。

來個驚喜

到這裏該講的差很少都說完了,可是由於個人項目中多處用到多行文本的省略,每次都copy實在非程序員所爲。因此,我將這個方法插入jQuery,做爲擴展的方法。將代碼貼出來,造福和我同樣萌萌的新人~

/**
 * jQuery自定義函數
 */
(function ($) {
    /**
     * 檢查文字長度是否溢出,若是是則將溢出部分省略
     *
     *   注意:
     *   文字自己容器需設置line-height
     *   父物體容器需設置height和overflow-wrap: break-word(目前兼容性比較好的溢出換行樣式)
     */
    $.fn.checkOverflow = function (parentClass) {
        $(this).each(function (id, el) {
            var $this = $(el);
            var $parent = $this.closest(parentClass);
            if ($this.height() > $parent.height()) {
                var parentHeight = $parent[0].offsetHeight;
                var thisText = $this.text();
                for (var i = 0; i <= thisText.length; i++) {
                    $this.text(thisText.slice(0, i));
                    if (parentHeight < $this[0].scrollHeight) {
                        var str_all_cn = true;
                        // 判斷省略號取代的三個字符是否全爲中文字符
                        thisText.slice(i-2).split('').forEach(function (c,id) {
                            if (thisText.slice(i-2).charCodeAt(id) <= 255) {
                                str_all_cn = false;
                            }
                        });
                        if (str_all_cn) {
                            $this.text(thisText.slice(0, i - 2) + "...");
                        } else {
                            $this.text(thisText.slice(0, i - 3) + "...");
                        }
                        break;
                    }
                }
            }
        })
    };
})(jQuery);
複製代碼

總結

雖然這個只是解決了實際項目開發中一個小得不能再小的問題,可是從頭至尾寫下來,我我的仍是從中收穫了不少。因此說,寫專欄一時爽,一直寫一直爽~

最後,推薦一些在這個過程當中發現的頗有用的資料:

(阮一峯)文檔:JavaScript 教程 / DOM

MDN 文檔:CSS Text

相關文章
相關標籤/搜索