I have already shared a way of implementing a “read-more” behavior truncating by the number of lines instead of the number of words.
But now I want to share how you can do it using the line-clamp
css property.
Your are going to need…
linesValue
to configure the number of lines that we want to display,hide
the buttons and a class to truncate
the content…content
, the moreButton
and the lessButton
…resize@window->read-more#render
- to calculate the truncation on rezise.click->read-more#showMore
click->read-more#showLess
html { line-height: 1.5 }
.hide { display: none; }
.line-clamp {
overflow : hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
}
<div data-controller="read-more"
data-read-more-lines-value="3"
data-read-more-hide-class="hide"
data-read-more-truncate-class="line-clamp"
data-action="resize@window->read-more#render">
<p data-read-more-target="content">
The content that you want to truncate...
</p>
<button class="hide"
data-read-more-target="moreButton"
data-action="read-more#showMore">
Show more
</button>
<button class="hide"
data-read-more-target="lessButton"
data-action="read-more#showLess">
Show less
</button>
</div>
You are going to use line-clamp
to truncate the content, assigning the configured linesValue
to the property -webkit-line-clamp
.
But you are going to do it just if the height
is greater that the expectedHeigt
that is the product of the linesValue
with the lineHeight
.
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
static targets = ["content", "moreButton", "lessButton"];
static classes = ["truncate", "hide"]
static values = { lines: Number }
connect() {
this.render()
}
render() {
this.showAllContent()
if (this.height() > this.expectedHeight()) {
this.showLess()
} else {
this.showAllContent()
this.hide(this.moreButtonTarget);
this.hide(this.lessButtonTarget);
}
}
showMore() {
this.showAllContent();
this.hide(this.moreButtonTarget);
this.show(this.lessButtonTarget);
}
showLess() {
this.truncateContent();
this.hide(this.lessButtonTarget);
this.show(this.moreButtonTarget);
}
showAllContent() {
this.contentTarget.classList.remove(this.truncateClass);
}
truncateContent() {
this.contentTarget.style["-webkit-line-clamp"] = this.linesValue;
this.contentTarget.classList.add(this.truncateClass);
}
show(target) {
target.classList.remove(this.hideClass)
}
hide(target) {
target.classList.add(this.hideClass)
}
lineHeight() {
let style = window.getComputedStyle(this.contentTarget)
return parseFloat(style.lineHeight, 10);
}
height() {
return this.contentTarget.offsetHeight;
}
expectedHeight() {
return this.linesValue * this.lineHeight();
}
}
If for some reason, you can’t use line-clamp
, maybe you can try doing the truncation with custom javascript.
And if you can truncate by number of characters, you can check this example using the “truncate” helper.
Here I try to share knowledge and fixes to common problems and struggles for ruby on rails developers, like How to fetch the latest-N-of-each record or How to test that an specific mail was sent or a Capybara cheatsheet. You can see more examples on Most recent posts or all posts.