with the flow

WEBプログラマを目指すWEBデザイナーが書き綴る開発日誌のようなもの

text-overflowはFirefox7から使える/複数行の時はJavaScriptで対応

まんまです。使えるようになってるのを最近知りました。

text-overflowはIEが6から独自仕様として実装していたものですが、
Microsoft珍しく素晴らしい先見性によって、CSS3の仕様にも組み込まれたものです。今までもwebkit,Operaでは使えていました。
それが、Firefox7からようやく実装されたということですね。

以下のクラスを作っておいて、横幅の指定された対象要素のクラスに追加してあげれば良いです。

.ellipsis {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  -webkit-text-overflow: ellipsis;
  -o-text-overflow: ellipsis;
}

はてなCSSが使えるようなので、以下実際にやってみますね。

わかりやすいようにボーダーで囲ってみます。

IE6〜,Firefox7.0〜,Opera,Chrome,Safariで見ると「…」が文末に表示されているはずです。

この文章は横幅400px以上になると省略記号が表示されます。この文章は横幅400px以上になると省略記号が表示されます。


これで今の主要ブラウザに対応できるというわけですね。

ただ、text-overflowは、文章が横1行の時しか使えないです。
当たり前ですが。


複数行に渡る文章の文字数を削りたいときはphpとかだとsubstr使ってこんな感じにするんでしょうし、
DBから抜いてくるときにLEFTで文字数指定したりするんでしょうけど、


・「行を含むp要素の高さが50pxを超えた時」など、ビジュアルを基準にして削りたい場合、マルチバイトの日本語だと全角と半角が入り乱れており、文字数をそのまま画面上の幅や高さとして考えることができない為難しい。

・文章の終端に「…」と書いたspan要素なんかを文字の上からz-indexで重ねて文字をもみ消す方法などもあるが、そもそも文章の背景が透明な場合(画像とか)は使えない。結局文字がはみ出すかどうかを判定するのにjs使うし。。。


といった問題があります。


このような場合は致し方ないので、JavaScriptで削るわけですが、みんなどうしてんだろ?と思ってgoogle先生に聞いてみました。
普通にググッても出て来ず、stackoverflowでやっと満足の行く回答を見つけたので、共有しておきます。


Insert ellipsis (…) into HTML tag if content too wide
http://stackoverflow.com/questions/536814/insert-ellipsis-into-html-tag-if-content-too-wide


詳しい内容はサイトを見ていただくとして、js部分だけ抜き出すと以下のようになります。

(function($) {
        $.fn.ellipsis = function()
        {
                return this.each(function()
                {
                        var el = $(this);

                        if(el.css("overflow") == "hidden")
                        {
                                var text = el.html();
                                var multiline = el.hasClass('multiline');
                                var t = $(this.cloneNode(true))
                                        .hide()
                                        .css('position', 'absolute')
                                        .css('overflow', 'visible')
                                        .width(multiline ? el.width() : 'auto')
                                        .height(multiline ? 'auto' : el.height())
                                        ;

                                el.after(t);

                                function height() { return t.height() > el.height(); };
                                function width() { return t.width() > el.width(); };

                                var func = multiline ? height : width;

                                while (text.length > 0 && func())
                                {
                                        text = text.substr(0, text.length - 1);
                                        t.html(text + "…");
                                }

                                el.html(t.html());
                                t.remove();
                        }
                });
        };
})(jQuery);

jQuery必須。

対象要素をコピーして、中身を一文字ずつ減らしていきその要素の高さがカットしたい高さと一致する部分を探すという方法。
bindされてるかもしれないイベントまでコピーする必要は無いので、jQueryのclone()ではなくJS標準のcloneNodeを使っているんだと解釈した。
ブラウザ・PCの性能が良くなった今だからこそできる力技ですね。

ただ一文字ずつ削っている為、対象要素が多いとさずがに負荷がかかります。
text = text.substr(0, text.length - 3)とかでもいいかもしれません。

#追記:
この方法は、記事のタイトルなど「出力される文字は基本的に少ないけど、時々多くなることがある」対象に対して使うのが吉。
記事本文などを削る場合は、削りたい文字数よりも若干多め(30文字以降削りたいなら35文字とか)にDBからLEFT使って吐き出しておいて、それを削るとかにしないと、ブラウザが悲鳴を上げるので注意。