jQuery で複数行対応の自動省略三点リーダー「...」付与修正版

# はじめに
jQuery で複数行対応の自動省略３点リーダーを付与します。わざわざプラグイン使うまでもないので、書いておきます。CSS でも可能ですが、:before や :after を使って右端に余白を設けて固定で「...」を書くなど、ちょっと見た目がださかったのでやめました。参考リンクを貼っておきますが、参考リンクの内容はウィンドウサイズを縮めたときにしか使えないですし、拡大したときに文章が枠内に収まったとしても「...」が残るなど、いろいろ未完成だったので修正しました。

- 参考
    - [文字列を省略して「...」を付与する方法](https://dev.classmethod.jp/ria/string-replace-css-and-jquery/)
    - [意外と需要がある（泣）文字数を省略して「...」にしたい。複数行のとき！](https://qiita.com/kiku-nao/items/339a8ea673c1917c6b59)

# 準備
jQuery を入れてください。

# コード

```javascript:style.js
<script type="text/javascript">
    $(function () {
        $(document).ready(function () {
            $('.video-title').each(function () {

                var $target = $(this);
                var rest = "";

                // オリジナルの文章を取得
                var html = $target.html();

                // 対象の要素を、高さにautoを指定し非表示で複製する
                var $clone = $target.clone();
                $clone
                    .css({
                        display: 'none',
                    })
                    .width($target.width())
                    .height('auto');

                // 目印クラスをつけて
                // DOMを追加
                $clone.addClass("video-title-rest");
                $target.after($clone);

                // 指定した高さになるまで、1文字ずつ消去していく
                while ((html.length > 0) && ($clone.height() > $target.height())) {
                    rest = html.substr(html.length - 1, 1) + rest;
                    html = html.substr(0, html.length - 1);
                    $clone.html(html + "..."); // 高さ更新
                }

                // 文章差し替え
                if (rest == "") {
                    $target.html(html);
                } else {
                    $target.html(html + '...');
                }
                // リサイズ用に次の要素に残りの文章を入れておく
                $clone.html(rest);
            });
        });

        var timer = false;
        $(window).resize(function () {
            // タイマーによって、リサイズ単位毎に関数が実行され、重くなるのを防ぐ
            if (timer !== false) {
                clearTimeout(timer);
            }
            timer = setTimeout(function () {
                $('.video-title').each(function () {

                    var $target = $(this);
                    var rest;
                    // 以前にリサイズしたか(document.readyで必ず<p class="video-title-rest">
                    // は存在するのでこの条件文はtrueを返すが、念のため)
                    if ($target.next().hasClass("video-title-rest")) {
                        // 省略していた文章を取得
                        rest = $target.next().html();
                        // 省略していた文章が空ではなかったとき、本文には３点リーダーが表示されて
                        // いるので、その３文字を削除
                        if (rest != "") {
                            $target.html($target.html().slice(0, -3)); // 末尾の...を削除
                        }
                        // これがないと永遠に<p class="video-title-rest">が増えていく
                        $target.next().remove();
                    } else {
                        rest = "";
                    }

                    // オリジナルの文章を復元
                    var html = $target.html() + rest;

                    // 対象の要素を、高さにautoを指定し非表示で複製する
                    // 方針としては、このクローン(オリジナルの文章を保持)を非表示でブラウザに配置させ、
                    // クローンの文字消去と元のボックスとの高さ比較を行うことによって、
                    // クローンが元のボックスと同様の高さになったときの文章で差し替える
                    var $clone = $target.clone();
                    $clone.html(html);
                    $clone
                        .css({
                            display: 'none',
                        })
                        .width($target.width())
                        .height('auto');

                    // 目印クラスをつけて
                    // DOMを追加 (これにより高さを獲得)
                    $clone.addClass("video-title-rest");
                    $target.after($clone);

                    rest = "";
                    // 指定した高さになるまで、1文字ずつ消去していくと同時に、
                    // 文章が完全消去されないように rest に退避させておく
                    while ((html.length > 0) && ($clone.height() > $target.height())) {
                        rest = html.substr(html.length - 1, 1) + rest;
                        html = html.substr(0, html.length - 1);
                        $clone.html(html + "..."); // 高さ更新
                    }

                    // 文章差し替え
                    // rest が空っぽということは、三点リーダーを表示する必要がないということ
                    if (rest == "") {
                        $target.html(html);
                    } else {
                        $target.html(html + '...');
                    }
                    // 次のリサイズ用に次の要素に残りの文章を入れておく
                    $clone.html(rest);
                });
            }, 200);
        });
    });
</script>
```

```css:style.css
p.video-title {
    font-size: 13px;
    display: block;
    height: 50px;
    margin: 0;
    overflow: hidden;
}
```

```html:index.html
<p class="video-title">あああああああああああああああああああああああああああああああああああああああああああああ</p>
```

全体のコードはこんな感じです。解説はコード内のコメントアウトで書いています。

## 前提
- \<p class="video-title">タグは親の\<div>タグなどで既に最大幅が決定している
- 私の開発現場では video-title としていますが、適宜自分に合ったクラス名に変更してください
- style.css の p.video-title の font-size や height を変更し、何行で表示するかを決定する

## 参考リンクとの差分
私が修正したのは、主に rest で表される残りの文章です。参考リンクの方法では、「...」で省略された残りの文章が消されてしまっていました。そのため、縮小を繰り返すと文章が短くなっていき、いざ拡大したときに表示するものがなくなっていました。

そこで、省略する文字列を rest に格納し、それを html の \<p class="video-title">タグの後ろに、\<p class="video-title-rest">タグ(非表示)の中に保存しておきます。そして、次回のリサイズ時に \<p class="video-title-rest"> の中に文章があるのかどうかをチェックします。もし文章があれば三点リーダーを使って省略されているということなので、\<p class="video-title"> の末尾の３文字(３点リーダー)を消去し、rest とくっつけることでオリジナルの文章を復元しています。後は繰り返しです。


## 注意点
- 私の場合は、$clone.css に position: abusolute; を設定していませんが、もし \<p class="video-title-rest">が原因で以降のレイアウトが崩れるようでしたら、設定しておくことをオススメします。absolute を設定すると、以降のタグは absolute を無視して空間を詰めてくれるはずです。
- あまりにも長い文章に対しては、１文字ずつ処理しているので不向きだと思います。

# おわりに
プラグインを使うまでもないとは言いましたが、簡単な処理の割には以外に複雑でした。他にも実現方法はいくらでもあると思いますが、１つの解として受け止めていただければ幸いです。