わかりやすくするために、この質問の画像を作成しました。
<div>
固定の幅と複数の線で省略記号を作成することは可能ですか?
いくつかのjQueryプラグインをあちこち試してみましたが、探しているプラグインが見つかりません。何かお勧めですか?アイデア?
わかりやすくするために、この質問の画像を作成しました。
<div>
固定の幅と複数の線で省略記号を作成することは可能ですか?
いくつかのjQueryプラグインをあちこち試してみましたが、探しているプラグインが見つかりません。何かお勧めですか?アイデア?
回答:
簡単な基本的なアイデアです。
私は次のマークアップでテストしていました:
<div id="fos">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin nisi ligula, dapibus a volutpat sit amet, mattis et dui. Nunc porttitor accumsan orci id luctus. Phasellus ipsum metus, tincidunt non rhoncus id, dictum a lectus. Nam sed ipsum a lacus sodales eleifend. Vestibulum lorem felis, rhoncus elementum vestibulum eget, dictum ut velit. Nullam venenatis, elit in suscipit imperdiet, orci purus posuere mauris, quis adipiscing ipsum urna ac quam.</p>
</div>
そしてCSS:
#fos { width: 300px; height: 190px; overflow: hidden; }
#fos p { padding: 10px; margin: 0; }
このjQueryを適用すると、望ましい結果が得られます。
var $p = $('#fos p');
var divh = $('#fos').height();
while ($p.outerHeight() > divh) {
$p.text(function (index, text) {
return text.replace(/\W*\s(\S)*$/, '...');
});
}
希望するサイズになるまで、テキストの最後の単語を繰り返し削除しようとします。オーバーフローのため:非表示。プロセスは目に見えないままであり、JSがオフになっていても、結果は「視覚的に正しい」ままです(もちろん「...」なし)。
これをサーバー側の適切な切り捨てと組み合わせると(オーバーヘッドがわずかに残ります)、実行速度が速くなります:)。
繰り返しますが、これは完全なソリューションではなく、単なるアイデアです。
更新:jsFiddleデモを追加しました。
li
内部にa .block
とa があり、.block h2
これをh2
内部に適用する必要がありますが、機能させる.block
ことができませんでした。複数ある場合、それは何か違い.block h2
ますか?
height*3
数ピクセル小さかったようです。 簡単な修正は、数ピクセルを単純に追加することですdivh
while
行:if(!$p.text().match(/\W*\s(\S)*$/)) break;
while ((upper-lower)>1) {let middle=((lower+upper)/2)|0 /*|0 is quick floor*/; if (test(words.slice(0,middle)+'...')) {lower=middle;} else {upper=middle;}}
。@KrisWebDevが検出したように、1つの巨大な単語もチェックする必要があります。
jQuery.dotdotdotプラグインを試してください。
$(".ellipsis").dotdotdot();
「ラインクランプ」は「複数行のブロックの省略記号」または「垂直省略記号」とも呼ばれることに注意してください。
github.com/BeSite/jQuery.dotdotdot
github.com/josephschmitt/Clamp.js
まだ調査していませんが、さらにいくつかあります。
CSSソリューションはいくつかありますが、-webkit-line-clamp
ブラウザのサポートが不十分な最も単純な使用法です。jsfiddle.net/AdrienBe/jthu55v7/でライブデモをご覧ください
CSSのみを使用してこれを実現するために多くの人々が多大な努力をしました。それに関する記事と質問を参照してください:
単純にする。この機能に専念する時間が十分にない場合は、最も単純でテスト済みのソリューションである、シンプルなCSSまたは十分にテストされたJavaScriptライブラリを使用してください。
ファンシー/複雑/高度にカスタマイズされた何かのために行くと、あなたはこの価格を将来支払うでしょう。
Airbnbのようにフェードアウトすることは、良い解決策かもしれません。おそらく、基本的なCSSと基本的なjQueryを組み合わせたものです。実際、それはCSSTricksのこのソリューションに非常に似ているようです
ああ、そしてデザインのインスピレーションを探すなら:
HTMLにはそのような機能はなく、これは非常にイライラします。
これに対処するためのライブラリを開発しました。
チェックアウト、私のサイトをスクリーンショット、チュートリアル、およびdowloadリンク用。
bažmegakapaのソリューションに基づく純粋なJSソリューション、および要素のlineHeight未満の高さ/最大高さを指定しようとする人を考慮したクリーンアップ:
var truncationEl = document.getElementById('truncation-test');
function calculateTruncation(el) {
var text;
while(el.clientHeight < el.scrollHeight) {
text = el.innerHTML.trim();
if(text.split(' ').length <= 1) {
break;
}
el.innerHTML = text.replace(/\W*\s(\S)*$/, '...');
}
}
calculateTruncation(truncationEl);
私はうまく機能する解決策を持っていますが、代わりに省略記号は勾配を使用しています。利点は、JavaScriptの計算を行う必要がなく、テーブルセルを含む可変幅コンテナーで機能することです。追加のdivをいくつか使用しますが、実装は非常に簡単です。
http://salzerdesign.com/blog/?p=453
編集:申し訳ありませんが、リンクが十分でないことを知りませんでした。解決策は、テキストの周りにdivを配置し、divをスタイルしてオーバーフローを制御することです。divの内側に、CSSまたは画像(古いIEの場合)を使用して作成できる「フェード」グラデーションの別のdivを配置します。グラデーションは透明から表のセルの背景色に変化し、省略記号よりも少し幅が広くなっています。テキストが長くてオーバーフローしている場合、「フェード」divの下に入り、「フェードアウト」しているように見えます。テキストが短い場合、フェードは見えないので問題ありません。2つのコンテナーは、コンテナーの高さをテキスト行の高さの倍数として設定することにより、1つまたは複数の行を表示するように調整できます。「フェード」divは、最後の行のみをカバーするように配置できます。
これを実現する純粋なCSSの方法を次に示します。http://www.mobify.com/blog/multiline-ellipsis-in-pure-css/
ここに要約があります:
<html>
<head>
<style>
html, body, p { margin: 0; padding: 0; font-family: sans-serif;}
.ellipsis {
overflow: hidden;
height: 200px;
line-height: 25px;
margin: 20px;
border: 5px solid #AAA; }
.ellipsis:before {
content:"";
float: left;
width: 5px; height: 200px; }
.ellipsis > *:first-child {
float: right;
width: 100%;
margin-left: -5px; }
.ellipsis:after {
content: "\02026";
box-sizing: content-box;
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
float: right; position: relative;
top: -25px; left: 100%;
width: 3em; margin-left: -3em;
padding-right: 5px;
text-align: right;
background: -webkit-gradient(linear, left top, right top,
from(rgba(255, 255, 255, 0)), to(white), color-stop(50%, white));
background: -moz-linear-gradient(to right, rgba(255, 255, 255, 0), white 50%, white);
background: -o-linear-gradient(to right, rgba(255, 255, 255, 0), white 50%, white);
background: -ms-linear-gradient(to right, rgba(255, 255, 255, 0), white 50%, white);
background: linear-gradient(to right, rgba(255, 255, 255, 0), white 50%, white); }
</style>
</head>
<body>
<div class="ellipsis">
<div>
<p>Call me Ishmael.....</p>
</div>
</div>
</body>
</html>
-webkit-line-clamp
プロパティはで使用できますdiv
。
-webkit-line-clamp: <integer>
つまり、コンテンツを切り捨てる前に最大行数を設定し(…)
、最後の行の最後に省略記号を表示します。
div {
width: 205px;
height: 40px;
background-color: gainsboro;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
/* <integer> values */
-webkit-line-clamp: 2;
}
<div>This is a multi-lines text block, some lines inside the div, while some outside</div>
以下は、ピンチで使用できる基本的なJavaScriptソリューションです。
// @param 1 = element containing text to truncate
// @param 2 = the maximum number of lines to show
function limitLines(el, nLines) {
var nHeight,
el2 = el.cloneNode(true);
// Create clone to determine line height
el2.style.position = 'absolute';
el2.style.top = '0';
el2.style.width = '10%';
el2.style.overflow = 'hidden';
el2.style.visibility = 'hidden';
el2.style.whiteSpace = 'nowrap';
el.parentNode.appendChild(el2);
nHeight = (el2.clientHeight+2)*nLines; // Add 2 pixels of slack
// Clean up
el.parentNode.removeChild(el2);
el2 = null;
// Truncate until desired nLines reached
if (el.clientHeight > nHeight) {
var i = 0,
imax = nLines * 35;
while (el.clientHeight > nHeight) {
el.innerHTML = el.textContent.slice(0, -2) + '…';
++i;
// Prevent infinite loop in "print" media query caused by
// Bootstrap 3 CSS: a[href]:after { content:" (" attr(href) ")"; }
if (i===imax) break;
}
}
}
limitLines(document.getElementById('target'), 7);
#test {
width: 320px;
font-size: 18px;
}
<div id="test">
<p>Paragraph 1</p>
<p id="target">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<p>Paragraph 3</p>
</div>
以下のコードペンでそれをいじることができます。CSSパネルでフォントサイズを変更し、HTMLパネルで少し編集して(どこかに余分なスペースを追加するなど)、結果を更新します。フォントサイズに関係なく、中央の段落は常に、limitLines()に渡される2番目のパラメータの行数に切り捨てられる必要があります。
Codepen: http ://codepen.io/thdoan/pen/BoXbEK
編集:与えられた最大の高さに基づいて複数行のテキストの切り捨てを本当にうまく行うJSプラグインであるShaveを渡って来ました。バイナリ検索を使用して、最適なブレークポイントを見つけます。間違いなく調査する価値があります。
元の回答:
私はこの問題のためのバニラJSソリューションを考え出す必要がありました。私が取り組んだ場合、長い製品名を限られた幅で2行に収める必要がありました。必要に応じて省略記号で切り捨てられます。
さまざまなSO投稿からの回答を使用して、自分のニーズに合ったものを調理しました。戦略は次のとおりです。
コードサンプル:
/**
* Helper to get the average width of a character in px
* NOTE: Ensure this is used only AFTER font files are loaded (after page load)
* @param {DOM element} parentElement
* @param {string} fontSize
*/
function getAverageCharacterWidth(parentElement, fontSize) {
var textSample = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()";
parentElement = parentElement || document.body;
fontSize = fontSize || "1rem";
var div = document.createElement('div');
div.style.width = "auto";
div.style.height = "auto";
div.style.fontSize = fontSize;
div.style.whiteSpace = "nowrap";
div.style.position = "absolute";
div.innerHTML = textSample;
parentElement.appendChild(div);
var pixels = Math.ceil((div.clientWidth + 1) / textSample.length);
parentElement.removeChild(div);
return pixels;
}
/**
* Helper to truncate text to fit into a given width over a specified number of lines
* @param {string} text Text to truncate
* @param {string} oneChar Average width of one character in px
* @param {number} pxWidth Width of the container (adjusted for padding)
* @param {number} lineCount Number of lines to span over
* @param {number} pad Adjust this to ensure optimum fit in containers. Use a negative value to Increase length of truncation, positive values to decrease it.
*/
function truncateTextForDisplay(text, oneChar, pxWidth, lineCount, pad) {
var ellipsisPadding = isNaN(pad) ? 0 : pad;
var charsPerLine = Math.floor(pxWidth / oneChar);
var allowedCount = (charsPerLine * (lineCount)) - ellipsisPadding;
return text.substr(0, allowedCount) + "...";
}
//SAMPLE USAGE:
var rawContainer = document.getElementById("raw");
var clipContainer1 = document.getElementById("clip-container-1");
var clipContainer2 = document.getElementById("clip-container-2");
//Get the text to be truncated
var text=rawContainer.innerHTML;
//Find the average width of a character
//Note: Ideally, call getAverageCharacterWidth only once and reuse the value for the same font and font size as this is an expensive DOM operation
var oneChar = getAverageCharacterWidth();
//Get the container width
var pxWidth = clipContainer1.clientWidth;
//Number of lines to span over
var lineCount = 2;
//Truncate without padding
clipContainer1.innerHTML = truncateTextForDisplay(text, oneChar, pxWidth, lineCount);
//Truncate with negative padding value to adjust for particular font and font size
clipContainer2.innerHTML = truncateTextForDisplay(text, oneChar, pxWidth, lineCount,-10);
.container{
display: inline-block;
width: 200px;
overflow: hidden;
height: auto;
border: 1px dotted black;
padding: 10px;
}
<h4>Untruncated</h4>
<div id="raw" class="container">
This is super long text which needs to be clipped to the correct length with ellipsis spanning over two lines
</div>
<h4>Truncated</h4>
<div id="clip-container-1" class="container">
</div>
<h4>Truncated with Padding Tweak</h4>
<div id="clip-container-2" class="container">
</div>
PS:
PPS:これは、@ DanManと@ st.neverによって提案されたアプローチに非常に似ていることに気づきました。実装例については、コードスニペットを確認してください。
非常にシンプルなJavaScriptソリューション。Divは、feのスタイルを設定する必要があります。
.croppedTexts {
max-height: 32px;
overflow: hidden;
}
そしてJS:
var list = document.body.getElementsByClassName("croppedTexts");
for (var i = 0; i < list.length; i++) {
cropTextToFit(list[i]);
}
function cropTextToFit (o) {
var lastIndex;
var txt = o.innerHTML;
if (!o.title) o.title = txt;
while (o.scrollHeight > o.clientHeight) {
lastIndex = txt.lastIndexOf(" ");
if (lastIndex == -1) return;
txt = txt.substring(0, lastIndex);
o.innerHTML = txt + "…";
}
}
質問に対する正確な回答ではありませんが、非常によく似ているのにこのページに出くわしましたが、単なる省略記号ではなく「もっと見る」へのリンクを追加したいと思っていました。これは、コンテナーからオーバーフローしているテキストに「more」リンクを追加するjQuery関数です。個人的に私はこれをBootstrapで使用していますが、もちろんそれは動作しません。
使用するには、次のようにテキストをコンテナに入れます。
<div class="more-less">
<div class="more-block">
<p>The long text goes in here</p>
</div>
</div>
次のjQuery関数が追加されると、adjustheightの値より大きいdivは切り捨てられ、「More」リンクが追加されます。
$(function(){
var adjustheight = 60;
var moreText = '+ More';
var lessText = '- Less';
$(".more-less .more-block").each(function(){
if ($(this).height() > adjustheight){
$(this).css('height', adjustheight).css('overflow', 'hidden');
$(this).parent(".more-less").append
('<a style="cursor:pointer" class="adjust">' + moreText + '</a>');
}
});
$(".adjust").click(function() {
if ($(this).prev().css('overflow') == 'hidden')
{
$(this).prev().css('height', 'auto').css('overflow', 'visible');
$(this).text(lessText);
}
else {
$(this).prev().css('height', adjustheight).css('overflow', 'hidden');
$(this).text(moreText);
}
});
});
これに基づくが、更新:http : //shakenandstirredweb.com/240/jquery-moreless-text
前述のdotdotdot jQueryプラグインは、angularでうまく機能します。
(function (angular) {
angular.module('app')
.directive('appEllipsis', [
"$log", "$timeout", function ($log, $timeout) {
return {
restrict: 'A',
scope: false,
link: function (scope, element, attrs) {
// let the angular data binding run first
$timeout(function() {
element.dotdotdot({
watch: "window"
});
});
}
}
}
]);
})(window.angular);
対応するマークアップは次のようになります。
<p app-ellipsis>{{ selectedItem.Description }}</p>
複数行の省略問題の解決策を検索したところ、jQueryなしには優れた解決策がないことに驚きました。また、「while」ループに基づくいくつかの解決策がありますが、無限ループに入る可能性があるため、効果的で危険ではないと思います。だから私はこのコードを書きました:
function ellipsizeTextBox(el) {
if (el.scrollHeight <= el.offsetHeight) {
return;
}
let wordArray = el.innerHTML.split(' ');
const wordsLength = wordArray.length;
let activeWord;
let activePhrase;
let isEllipsed = false;
for (let i = 0; i < wordsLength; i++) {
if (el.scrollHeight > el.offsetHeight) {
activeWord = wordArray.pop();
el.innerHTML = activePhrase = wordArray.join(' ');
} else {
break;
}
}
let charsArray = activeWord.split('');
const charsLength = charsArray.length;
for (let i = 0; i < charsLength; i++) {
if (el.scrollHeight > el.offsetHeight) {
charsArray.pop();
el.innerHTML = activePhrase + ' ' + charsArray.join('') + '...';
isEllipsed = true;
} else {
break;
}
}
if (!isEllipsed) {
activePhrase = el.innerHTML;
let phraseArr = activePhrase.split('');
phraseArr = phraseArr.slice(0, phraseArr.length - 3)
el.innerHTML = phraseArr.join('') + '...';
}
}
let el = document.getElementById('ellipsed');
ellipsizeTextBox(el);
かなり遅いかもしれませんが、SCSSを使用すると、次のような関数を宣言できます。
@mixin clamp-text($lines, $line-height) {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: $lines;
line-height: $line-height;
max-height: unquote('#{$line-height*$lines}em');
@-moz-document url-prefix() {
position: relative;
height: unquote('#{$line-height*$lines}em');
&::after {
content: '';
text-align: right;
position: absolute;
bottom: 0;
right: 0;
width: 30%;
height: unquote('#{$line-height}em');
background: linear-gradient(
to right,
rgba(255, 255, 255, 0),
rgba(255, 255, 255, 1) 50%
);
}
}
}
そしてそれを次のように使用します:
.foo {
@include clamp-text(1, 1.4);
}
これにより、テキストが1行に切り捨てられ、テキストの行の高さが1.4であることがわかります。予想される出力は...
、最後にレンダリングするクロムと、最後にクールなフェードを伴うFFです。
Firefox
クロム
Adrien Beの答えでこの短いCSSのみのソリューションを見つけました:
.line-clamp {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
2020年3月の 時点で、ブラウザーのサポートは95.3%であり、IEおよびOpera Miniではサポートされていません。Chrome、Safari、Firefox、Edgeで動作します。
@DanManのソリューションを拡張するには、可変幅フォントが使用されている場合、平均フォント幅を使用できます。これには2つの問題があります。1)Wが多すぎるテキストはオーバーフローし、2)Iが多すぎるテキストは先に切り捨てられます。
または、最悪のケースのアプローチをとり、文字 "W"の幅を使用することもできます(これが最も広いと思います)。これにより、上記の問題1は削除されますが、問題2が激化します。
別の方法としてはoverflow: clip
、divをそのままにして、float: right; position: relative; bottom: 0px;
(テストされていない)で省略記号セクション(別のdivまたは画像)を追加します。コツは、画像をテキストの最後に表示することです。
オーバーフローすることがわかっている場合にのみ、画像を表示することもできます(たとえば、約100文字の後)。
overflow: clip
?そして、あなたはそのCSSで何をすることを期待float
しますか?
このコードでは、要素の高さがmax-heightスタイルによって制限されている場合、追加のラッパーdivは必要ありません。
// Shorten texts in overflowed paragraphs to emulate Operas text-overflow: -o-ellipsis-lastline
$('.ellipsis-lastline').each(function(i, e) {
var $e = $(e), original_content = $e.text();
while (e.scrollHeight > e.clientHeight)
$e.text($e.text().replace(/\W*\w+\W*$/, '…'));
$e.attr('data-original-content', original_content);
});
また、スタイルのみを使用して表示できるデータ属性に元のテキストを保存します。マウスオーバー時:
.ellipsis-lastline {
max-height: 5em;
}
.ellipsis-lastline:before {
content: attr(data-original-content);
position: absolute;
display: none;
}
.ellipsis-lastline:hover:before {
display: block;
}
私のシナリオでは、上記の関数をどれも動作させることができず、フォントサイズやコンテナサイズに関係なく、表示する行数を関数に通知する必要もありました。
私の解決策は、ここでDomiが説明しているCanvas.measureTextメソッド(これはHTML5機能です)の使用に基づいているため、完全にクロスブラウザではありません。
このフィドルでどのように機能するかがわかります。
これはコードです:
var processTexts = function processTexts($dom) {
var canvas = processTexts .canvas || (processTexts .canvas = document.createElement("canvas"));
$dom.find('.block-with-ellipsis').each(function (idx, ctrl) {
var currentLineAdded = false;
var $this = $(ctrl);
var font = $this.css('font-family').split(",")[0]; //This worked for me so far, but it is not always so easy.
var fontWeight = $(this).css('font-weight');
var fontSize = $(this).css('font-size');
var fullFont = fontWeight + " " + fontSize + " " + font;
// re-use canvas object for better performance
var context = canvas.getContext("2d");
context.font = fullFont;
var widthOfContainer = $this.width();
var text = $.trim(ctrl.innerHTML);
var words = text.split(" ");
var lines = [];
//Number of lines to span over, this could be calculated/obtained some other way.
var lineCount = $this.data('line-count');
var currentLine = words[0];
var processing = "";
var isProcessing = true;
var metrics = context.measureText(text);
var processingWidth = metrics.width;
if (processingWidth > widthOfContainer) {
for (var i = 1; i < words.length && isProcessing; i++) {
currentLineAdded = false;
processing = currentLine + " " + words[i];
metrics = context.measureText(processing);
processingWidth = metrics.width;
if (processingWidth <= widthOfContainer) {
currentLine = processing;
} else {
if (lines.length < lineCount - 1) {
lines.push(currentLine);
currentLine = words[i];
currentLineAdded = true;
} else {
processing = currentLine + "...";
metrics = context.measureText(processing);
processingWidth = metrics.width;
if (processingWidth <= widthOfContainer) {
currentLine = processing;
} else {
currentLine = currentLine.slice(0, -3) + "...";
}
lines.push(currentLine);
isProcessing = false;
currentLineAdded = true;
}
}
}
if (!currentLineAdded)
lines.push(currentLine);
ctrl.innerHTML = lines.join(" ");
}
});
};
(function () {
$(document).ready(function () {
processTexts($(document));
});
})();
そして、それを使用するHTMLは次のようになります。
<div class="block-with-ellipsis" data-line-count="2">
VERY LONG TEXT THAT I WANT TO BREAK IN LINES. VERY LONG TEXT THAT I WANT TO BREAK IN LINES.
</div>
font-familyを取得するコードはかなり単純で、私の場合は機能しますが、より複雑なシナリオでは、これらの線に沿って何かを使用する必要がある場合があります。
また、私の場合、関数に使用する行数を指定していますが、コンテナーのサイズとフォントに応じて、表示する行数を計算できます。
私はhtmlをそのまま残すバージョンを作りました。 jsfiddleの例
jQuery
function shorten_text_to_parent_size(text_elem) {
textContainerHeight = text_elem.parent().height();
while (text_elem.outerHeight(true) > textContainerHeight) {
text_elem.html(function (index, text) {
return text.replace(/(?!(<[^>]*>))\W*\s(\S)*$/, '...');
});
}
}
$('.ellipsis_multiline').each(function () {
shorten_text_to_parent_size($(this))
});
CSS
.ellipsis_multiline_box {
position: relative;
overflow-y: hidden;
text-overflow: ellipsis;
}
この問題を解決する角度コンポーネントを作成しました。指定されたテキストをスパン要素に分割します。レンダリング後、オーバーフローしている要素をすべて削除し、最後の表示要素の直後に省略記号を配置します。
使用例:
<app-text-overflow-ellipsis [text]="someText" style="max-height: 50px"></app-text-overflow-ellipsis>
Stackblitzデモ:https ://stackblitz.com/edit/angular-wfdqtd
コンポーネント:
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef, HostListener,
Input,
OnChanges,
ViewChild
} from '@angular/core';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'app-text-overflow-ellipsis',
template: `
<span *ngFor="let word of words; let i = index" [innerHTML]="word + (!endsWithHyphen(i) ? ' ' : '')"></span>
<span #ellipsis [hidden]="!showEllipsis && !initializing" [class.initializing]="initializing" [innerHTML]="'...' + (initializing ? ' ' : '')"></span>
`,
styles: [`
:host {
display: block;
position: relative;
}
.initializing {
opacity: 0;
}
`
]
})
export class TextOverflowEllipsisComponent implements OnChanges {
@Input()
text: string;
showEllipsis: boolean;
initializing: boolean;
words: string[];
@ViewChild('ellipsis')
ellipsisElement: ElementRef;
constructor(private element: ElementRef, private cdRef: ChangeDetectorRef) {}
ngOnChanges(){
this.init();
}
@HostListener('window:resize')
init(){
// add space after hyphens
let text = this.text.replace(/-/g, '- ') ;
this.words = text.split(' ');
this.initializing = true;
this.showEllipsis = false;
this.cdRef.detectChanges();
setTimeout(() => {
this.initializing = false;
let containerElement = this.element.nativeElement;
let containerWidth = containerElement.clientWidth;
let wordElements = (<HTMLElement[]>Array.from(containerElement.childNodes)).filter((element) =>
element.getBoundingClientRect && element !== this.ellipsisElement.nativeElement
);
let lines = this.getLines(wordElements, containerWidth);
let indexOfLastLine = lines.length - 1;
let lineHeight = this.deductLineHeight(lines);
if (!lineHeight) {
return;
}
let indexOfLastVisibleLine = Math.floor(containerElement.clientHeight / lineHeight) - 1;
if (indexOfLastVisibleLine < indexOfLastLine) {
// remove overflowing lines
for (let i = indexOfLastLine; i > indexOfLastVisibleLine; i--) {
for (let j = 0; j < lines[i].length; j++) {
this.words.splice(-1, 1);
}
}
// make ellipsis fit into last visible line
let lastVisibleLine = lines[indexOfLastVisibleLine];
let indexOfLastWord = lastVisibleLine.length - 1;
let lastVisibleLineWidth = lastVisibleLine.map(
(element) => element.getBoundingClientRect().width
).reduce(
(width, sum) => width + sum, 0
);
let ellipsisWidth = this.ellipsisElement.nativeElement.getBoundingClientRect().width;
for (let i = indexOfLastWord; lastVisibleLineWidth + ellipsisWidth >= containerWidth; i--) {
let wordWidth = lastVisibleLine[i].getBoundingClientRect().width;
lastVisibleLineWidth -= wordWidth;
this.words.splice(-1, 1);
}
this.showEllipsis = true;
}
this.cdRef.detectChanges();
// delay is to prevent from font loading issues
}, 1000);
}
deductLineHeight(lines: HTMLElement[][]): number {
try {
let rect0 = lines[0][0].getBoundingClientRect();
let y0 = rect0['y'] || rect0['top'] || 0;
let rect1 = lines[1][0].getBoundingClientRect();
let y1 = rect1['y'] || rect1['top'] || 0;
let lineHeight = y1 - y0;
if (lineHeight > 0){
return lineHeight;
}
} catch (e) {}
return null;
}
getLines(nodes: HTMLElement[], clientWidth: number): HTMLElement[][] {
let lines = [];
let currentLine = [];
let currentLineWidth = 0;
nodes.forEach((node) => {
if (!node.getBoundingClientRect){
return;
}
let nodeWidth = node.getBoundingClientRect().width;
if (currentLineWidth + nodeWidth > clientWidth){
lines.push(currentLine);
currentLine = [];
currentLineWidth = 0;
}
currentLine.push(node);
currentLineWidth += nodeWidth;
});
lines.push(currentLine);
return lines;
}
endsWithHyphen(index: number): boolean {
let length = this.words[index].length;
return this.words[index][length - 1] === '-' && this.words[index + 1] && this.words[index + 1][0];
}
}
ここでは、より高速なアルゴリズムで別のライブラリを作成しました。チェックしてください:
https://github.com/i-ahmed-biz/fast-ellipsis
bowerを使用してインストールするには:
bower install fast-ellipsis
npmを使用してインストールするには:
npm install fast-ellipsis
お楽しみください!
これがあなたが探しているものかどうかはわかりませんが、高さの代わりに最小高さを使用します。
<div id="content" style="min-height:10px;width:190px;background:lightblue;">
<?php
function truncate($text,$numb) {
// source: www.kigoobe.com, please keep this if you are using the function
$text = html_entity_decode($text, ENT_QUOTES);
if (strlen($text) > $numb) {
$text = substr($text, 0, $numb);
$etc = "...";
$text = $text.$etc;
}
$text = htmlentities($text, ENT_QUOTES);
return $text;
}
echo truncate("this is a multi-lines text block, some lines inside the div, while some outside", 63);
?>
</div>
非常に単純なfuncで十分です。
指令:
$scope.truncateAlbumName = function (name) {
if (name.length > 36) {
return name.slice(0, 34) + "..";
} else {
return name;
}
};
見る:
<#p>{{truncateAlbumName(album.name)}}<#/p>
iiiiiiiiii
vsを参照してくださいMMMMMMMMMM
(現在のフォントは:Dでは見えません)。