ありそうでなかったテキスト省略ライブラリ Eripusisu を作った
まえがき
長すぎるテキストを「…」のような文字を使って省略表現することがあります。この実装は通常 CSS プロパティを使って実現していましたが、問題も多くありました。
次に挙げるものは長年、不便を感じていたり、実装都合でデザイナーからの提案を退けざるをえなかったりして、改善したいと思っていた問題です。
- 複数行の省略を行う標準的なやりかたが存在しない
- 中身がすべてインライン要素である必要がある
- すなわち
display: block
やdisplay: inline-block
な要素が混在できない - 省略した場合、視覚的に得られる情報と、スクリーンリーダー等で得られる情報が食い違ってしまう
省略対象のコンテンツが強く制約を受けてしまうことは特に、デザイナーを説得しづらく、解決したい問題でした。
そこで Eripusisu を作成しました。2020 年の今になってどれほど価値のあるライブラリなのかは未知数ですが、個人的には長年欲しかったヤツなので、刺さる人には刺さるのではないでしょうか。
特長
Eripusisu は次のような特長を備えています。
- 高さではなく、行数を指定して省略表示できる
- 内容の要素、スタイルを問わない
- 省略文字列を指定可能(複数文字も可)
- アクセシビリティに配慮
- RTL 言語サポート
デモ
簡易的なデモを用意しました。
キャプチャを添えて少し解説してみます。
通常の省略
これまで display: -webkit-box
を使ってモダンブラウザでは実現できていたパターンです。
展開したところ。
途中にリンクがある場合
これも特に驚きはありません。
展開したところ。
ブロック要素が含まれる場合
このあたりからは CSS や既存のテキスト省略系ライブラリでは対応できていなかった範囲です。インライン要素とブロック要素が混在していても、指定した行数のみ表示され、残りは省略されます。
展開したところ。
float した要素が含まれる場合
float した要素は float したままで、指定行より後が省略されます。float の左右方向は問いません。
展開したところ。
画像のみ含まれる場合
テキスト内に画像が含まれていても問題ありません。ここでは1行指定しています。
展開したところ。
inline-block な要素が含まれる場合
text-overflow
プロパティを使用した場合、省略対象の要素はすべてインライン要素である必要がありますが、Eripusisu を使うと inline-block な要素が含まれていても省略表示ができます。使える領域を最大限に使うように表示文字数を決定します。
なお、inline-block な要素の外側に省略文字「…」を表示することには未対応です。
展開したところ。
RTL 言語の場合
アラビア語などの RTL 書字方向をもつ言語にも対応しています。
展開したところ。
インストール
script 要素で読み込む
<script src="https://unpkg.com/eripusisu@1.1.2/dist/eripusisu.umd.js"></script>
npm パッケージを利用する
npm install --save Eripusisu
var Eripusisu = require("eripusisu");
使いかた
次のような HTML を想定して解説しています。
<div class="container">
<!-- 任意の省略対象の要素(群) -->
</div>
<button class="button">開閉</button>
<div class="container">
<!-- 任意の省略対象の要素(群) -->
</div>
<button class="button">開閉</button>
:
:
シンプルな省略表示
省略表示をしたいコンテンツを含むコンテナー要素を取得し、Eripusisu
コンストラクタ―の第1引数に渡します。第2引数には省略表示にする行数を指定します。
// 省略表示をしたいコンテンツを含むコンテナー要素を取得する
var container = document.querySelector(".container");
// Eripusisu を実行する(3行で省略)
new Eripusisu(container, 3);
クラス名から複数のコンテナーを省略表示
// 省略表示をしたいコンテンツを含むコンテナー要素を取得する
var containers = document.querySelectorAll(".container");
// ループを利用して Eripusisu を実行する
for (var i = 0; i < containers.length; i += 1) {
new Eripusisu(containers[i], 3);
}
開閉ボタン
第3引数の toggleButton
プロパティに要素を渡すことで開閉ボタンが動作します。
// 省略表示をしたいコンテンツを含むコンテナー要素を取得する
var container = document.querySelector(".container");
// ボタン要素を取得する
var button = document.querySelector(".button");
// Eripusisu を実行する
new Eripusisu(container, 3, { toggleButton: button });
幅の変化に追随させる
設計上、幅の変化には自動的に追随しません。必要があれば、resize
イベントをハンドリングするなどして refresh()
メソッドを呼び出します。
var eripusisu = new Eripusisu(container, 3);
window.addEventListener("resize", function () {
eripusisu.refresh();
})
コンストラクタ、メソッド、イベントの定義
README をご覧ください。
サポートブラウザ
IE 11 以降のモダンブラウザに対応しています。
アクセシビリティへの配慮
大したことではありませんが、開閉をトグルするボタンに WAI-ARIA の属性を付与しています。スクリーンリーダー利用時には、開閉の状態が読み上げられるようになっています。
ときどき、大きなコンテンツを overflow
プロパティを利用して視覚的にだけ隠している場合があります。このパターンをスクリーンリーダー等でアクセスすると、内容をすべて読み上げることになり、得られる情報に差が生まれるばかりか、場合によっては非常に使いにくいことがあります。Eripusisu ではあふれた部分は DOM から消えるため、情報の差異はなくなります。もこれも広義にはアクセシビリティへの配慮に含まれるかもしれません。
実行パフォーマンスについて
コンテンツが指定行内に収まるかどうかの判定はとても難しく、ブラウザに描画させた結果に対して行をまたぐかどうか判定する必要があります。テキスト長を変えながら繰り返し判定処理を行うため、処理は重くなりがちです。
できる限り軽量に動かすため、いくつかの対策をしています。
- 収まるテキスト長を二分探索法で割り出す
- 計算中、CSS Containment を指定してレイアウトの影響範囲を小さくする
- 計測結果をできるだけ再利用する
高速化の余地はまだあるかもしれません。お心当たりがあればぜひコントリビューションを。
コントリビューション歓迎
ぜひ使ってみて、使い心地をフィードバックください。GitHub プロジェクトへの issue, pull request 等も大歓迎です。