要素からはみでたら文字を丸めるJSプラグインを作成

# 概要

WEB画面を開発していて、文字数が多くて要素からはみ出ることはありませんか？
そんなとき、CSSで`overflow:hidden;`を設定したり、JSで文字数を制御していたりするかと思います。

今回は、アプローチを少し変え、「スクロールが発生したらはみ出た部分の文字列を削り、末尾の文字も指定ができる」JavaScriptのプラグインを作成しました。


# リポジトリ

リンクはこちらとなります。

https://github.com/ishi720/textfitJS


# インストール方法

インストールの方法は以下の通りです。

```bash
npm i textfitjs
```


# 使用方法

```js
// 初期化する
const str1 = new textFit(".str1");

// textCut()を実行（引数は末尾の文字列となる）
str1.textCut('【終了】');

// textShrinkCut()を実行（第一引数は末尾の文字。第二引数は文字サイズの下限）
str1.textShrinkCut('..など', 15);

// テキストや文字サイズをもとに戻す
str1.restoreOriginalText();

// もともとのテキストを返す
str1.originalText();

// もともとの文字サイズを返す
str1.originalFontSize();
```



# 動作イメージ

2つのメソッドを用意しました。

|str1.textCut('【終了】');|str1.textShrinkCut('..など', 15);|
|:-|:-|
|要素から文字がはみ出たら文字を、文字を削り、引数の文字を末尾につけます。|要素から文字がはみ出たら小さくします。それでもはみ出る場合は、文字を削り引数の文字を末尾につけます。|
|![Animation4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/473097/31b620af-54ac-d23c-2a96-d75d3c3f0e01.gif)|![Animation4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/473097/1e3fd540-f0b9-051c-b865-5d5afbdd2c17.gif)|

# サンプルページ

実際に触ってみたい方はこちらからどうぞ！

https://ishi720.github.io/textfitJS/


# 書いたプログラム

```js
"use strict";
class textFit {
    #orgText;
    #orgSize;

    constructor(selector) {
        this.selector = selector;
        this.targetEle = document.querySelector(this.selector);
        this.#orgText = this.targetEle.innerHTML;
        this.#orgSize = window.getComputedStyle(this.targetEle, null).getPropertyValue('font-size');
    }

    #isProtrudeWidth(ele) {
        const orgScrollLeft = ele.scrollLeft;
        ele.scrollLeft = 0;
        ele.scrollLeft = 1;
        const result = (ele.scrollLeft > 0);
        ele.scrollLeft = orgScrollLeft;
        return result;    
    }

    #isProtrudeHeight(ele) {
        const orgScrollTop = ele.scrollTop;
        ele.scrollTop = 0;
        ele.scrollTop = 1;
        const result = (ele.scrollTop > 0);
        ele.scrollTop = orgScrollTop;
        return result;
    }

    /**
    * 要素から文字がはみ出たら小さくする
    * @param leader 省略文字
    * @param repeat 繰り返す上限値
    */
    textShrinkCut(leader = "…", repeat = 10) {
        //一時的にスクロールバーをつける
        this.targetEle.style.overflowY = "hidden";

        for (let i = 0; this.#isProtrudeHeight(this.targetEle) === true || i >= repeat ; i++) {

            if (i >= repeat - 1) {
                //文字を省略する
                this.textCut(leader);
                break;
            }

            //下限判定(メモ：Chromeの最小値が10px)
            if (parseInt(this.targetEle.style.fontSize) <= 10) {
                //文字を省略する
                this.textCut();
                break;

            } else {
                //サイズを調整
                const el = this.targetEle;
                const fontSize = window.getComputedStyle(el, null).getPropertyValue('font-size');
                this.targetEle.style.fontSize = (parseFloat(fontSize) - 1) + 'px';
            }
        }
        //スクロールバーを消す
        this.targetEle.style.overflowY = "visible";
    }

    /**
    * 要素から文字がはみ出たら後ろの文字を消して…にする
    * @param leader 省略文字
    */
    textCut(leader = "…"){
        document.querySelectorAll(this.selector).forEach((element) => {
            let str = element.innerHTML;
            //一時的にスクロールバーをつける
            element.style.overflowY = 'hidden';
            while (this.#isProtrudeHeight(element) && str.length > 0) {
                //末尾の文字を削る
                str = str.slice(0, -1); 
                //文字をセットする
                element.innerHTML = str + leader;
            }
            //スクロールバーを消す
            element.style.overflowY = 'visible';
        });
    }

    /**
    * 要素のテキストとフォントサイズを元に戻す
    */
    restoreOriginalText() {
        this.targetEle.innerHTML = this.#orgText;
        this.targetEle.style.fontSize = this.#orgSize;
    }

    // 元のテキストのgetter
    get originalText() {
        return this.#orgText;
    }

    // 元のフォントサイズのgetter
    get originalFontSize() {
        return this.#orgSize;
    }
}
```

# コード解説

## スクロールが発生しているか判定

縦方向にスクロールが発生しているかチェックしています。
使っていませんが、横スクロールも実装しています。

```js
#isProtrudeHeight(ele) {
    const orgScrollTop = ele.scrollTop;
    ele.scrollTop = 0;
    ele.scrollTop = 1;
    const result = (ele.scrollTop > 0);
    ele.scrollTop = orgScrollTop;
    return result;
}
```

## もともとの状態を保持

処理する前の文字や文字サイズを保持しています。
これにより、機能追加することで、もともとの状態に戻したり、全文表示などが可能です。

```js
constructor(selector) {
    this.#orgText = this.targetEle.innerHTML;
    this.#orgSize = window.getComputedStyle(this.targetEle, null).getPropertyValue('font-size');
}
```

## 文字を削る処理

下記はtextCut()内の処理です。
スクロールがなくなるまで文字を削る処理を行っています。
また、末尾に文字をつけた状態で判定しているため、末尾の文字も要素内に収まようになっています。

```js
while (this.#isProtrudeHeight(element) && str.length > 0) {
    //末尾の文字を削る
    str = str.slice(0, -1); 
    //文字をセットする
    element.innerHTML = str + leader;
}
```

# 利用用途

上記サンプルでは、ボタンで操作するようにしていましたが、画面表示時やサイズの変更などのイベントに仕込むことで様々な画面サイズのディスプレイで効果が発揮されるかと思います。

また、全文を表示されていないため、「ツールチップ」や「もっと見る」で全文を取り出して表示する機能も作成可能です。


# おわりに

JavasScriptを使って、要素からはみでたら文字を丸めるプラグインを作成しました。

バニラなJavaScriptでプラグイン開発することで、ほかのライブラリに依存しない汎用的な機能ができます。
ちょっとした機能でも、1つのプラグインとして公開できるのが、プラグイン開発として面白い所です。

ぜひ使ってみてください！

ここまで読んでいただきありがとうございました。