しかし、文字数による判定では英語や日本語による違い、等幅フォントやプロポーショナルフォントによる違いを考慮しなければならない。
バイト数による判定も、同様の違いや文字コードによる違いがある。
「テキストが表示されたときの幅」を取得し、その「幅」で判定できれば、それが一番正確だろう。
ということで、動的に表示されるテキストの幅(width)を取得し、幅にあわせてスタイルを適用できるようにする。
テキストの幅を取得する
テキストの幅を取得する方法は、以下の手順で行う。
- span要素を作成し、テキストを設定する
- bodyなどに一時的にspan要素を追加する
- HTMLElement.offsetWidthで要素の幅を取得する
- bodyに追加したspan要素を削除する
このようにしてHTMLElement.offsetWidthを使うことで、テキストの幅を取得できる。
その際、注意すべき点は3つ。
1つ目は、spanタグのようなinline要素を使うこと。
pタグのようなblock要素を使うと、親要素の横幅いっぱいに広がってしまうため、正確な「テキストの幅」が取得できない。
2つ目は、offsetWidthにはborder、padding、スクロールバーの幅が含まれること。(marginは含まれない)
もしCSSで要素型セレクタ(タグ指定)しているのなら、追加するspan要素に{ border: none; padding: 0; overflow: hidden; }を付けておくとよい。
3つ目は、DOMに追加しないとoffsetWidthが取得できないこと。
メモリ上にある要素では幅が取得できないので、一旦bodyなどに追加する必要がある。
ただ、要素の削除に失敗するとテキストが残ってしまうので、 { visibility: hidden; }を使って対策する。
{ display: none; }だと要素が完全に消えてしまうoffsetWidthが取得できないので注意。
動的なテキスト幅を取得し、スタイルを適用する
幅75pxの要素にテキストを表示するサンプル。
条件は以下のとおり。
- できる限り省略記号を使わず、文字間隔を詰めてすべて表示する
- 文字間隔を調整しても収まらない場合には、省略記号を使って表示する
<ul class="list">
<!-- Sample
<li class="item">
<p class="title -compress -ellipsis">あいうえお</p>
</li>
-->
</ul>
.list {
display: flex;
flex-wrap: wrap;
padding: 10px;
}
.list > .item {
margin: 10px;
}
.item {
width: 75px;
height: 30px;
box-shadow: 0 1px 3px rgba(0,0,0,.3);
background-color: white;
}
.item > .title {
overflow: hidden;
text-align: center;
line-height: 30px;
}
/* 文字間隔の調整 */
.title.-compress {
letter-spacing: -4px;
}
/* 省略記号の表示 */
.title.-ellipsis {
white-space: nowrap;
text-overflow: ellipsis;
}
// 表示するテキストを作成
const generateTitle = i => {
const chars = ['あ', 'い', 'う', 'え', 'お'];
let title = '';
[...Array(i)].forEach((a, j) => title += chars[j % 5]);
return title;
};
// テキストの幅を取得
const getTextWidth = text => {
const span = document.createElement('span');
span.innerText = text;
span.style.visibility = 'hidden';
document.body.appendChild(span);
// offsetWidthができたらすぐに消す
setTimeout(() => {
document.body.removeChild(span);
}, 0);
return span.offsetWidth || 0;
};
// メイン
const main = () => {
const list = document.getElementsByClassName('list')[0];
// 10回ループする
[...Array(10)].forEach((a, i) => {
const item = document.createElement('li');
item.classList.add('item');
const title = document.createElement('p');
title.classList.add('title');
const text = generateTitle(i + 1);
const width = getTextWidth(text);
if (80 <= width && width <= 96) {
// 5〜6文字
title.classList.add('-compress');
} else if (96 < width) {
// 7文字以上
title.classList.add('-compress', '-ellipsis');
}
title.innerText = text;
item.appendChild(title);
list.appendChild(item);
});
};
main();
参考サイト
以上
written by @bc_rikko
0 件のコメント :
コメントを投稿