stimulus-read-more icon indicating copy to clipboard operation
stimulus-read-more copied to clipboard

Read more should not be visible if it is not truncated

Open woto opened this issue 4 years ago • 5 comments

Hi, it is not a bug. It is a feature request. It would be cool if toggle button would be not visible if content target already displays full test. What do you think?

Kind regards

woto avatar Mar 29 '21 01:03 woto

Not that easy but could be a nice addition

Could you open a PR? I will try to code that later too

guillaumebriday avatar Sep 10 '21 07:09 guillaumebriday

Would love this feature too. How can it be detected?

sedubois avatar Oct 09 '23 10:10 sedubois

Hey there.. just stumbled upon this issue.

I have 2 different ideas, that may be suitable to solve this issue:

1) Working with ScrollHeight

I guess, you could use the offsetHeight and scrollHeight properties of the content element, like so (pseudo-code - not sure if this works)

// element where the truncated content is rendered to
var element = document.querySelector('div');

if (element.scrollHeight > element.offsetHeight || // y-truncation
  element.scrollWidth > element.offsetWidth) { // x-truncation
  // the content was truncated - show button
} else {
  // no content-overflow - hide button
}

2) Checking for the content

You could also add the "original" (non-truncated) text to another variable (i.e. data-original-content) and pass it to the stimulus component. Then you could check:

if (truncatedContent == originalContent) {
  // both contents are the same - hide button
} else {
  // the truncatedContent is obviously "smaller" than the original content - show button
}

Guess, that could work as well..

I guess, solution 1 would be cleaner, because it would also work with additional HTML content (i.e,. tags / images within the content).

All the best Johannes

PS: i did not test any of these approaches ;)

johannesschobel avatar Oct 10 '23 07:10 johannesschobel

Yes this works:

<div data-controller="read-more"
     data-read-more-more-text-value="Read more"
     data-read-more-less-text-value="Read less">
  <div class="line-clamp-6" data-read-more-target="content">
    text to truncate goes here
  </div>
  <button data-action="read-more#toggle" data-read-more-target="button"></button>
</div>
import {Controller} from "@hotwired/stimulus"
import {useResize} from "stimulus-use"

export default class extends Controller {
  static targets = ["content", "button"]
  static values = { moreText: String, lessText: String }

  connect() {
    this.buttonTarget.innerHTML = this.moreTextValue
    useResize(this)
    this.open = false
  }

  toggle() {
    this.open = !this.open
    this.buttonTarget.innerHTML = this.open ? this.lessTextValue : this.moreTextValue
    this.contentTarget.classList.toggle("line-clamp-6", !this.open)
  }

  resize() {
    if (!this.open) {
      this.buttonTarget.classList.toggle("hidden", this.contentTarget.scrollHeight <= this.contentTarget.clientHeight)
    }
  }
}

sedubois avatar Oct 13 '23 13:10 sedubois

Hi @sedubois! Thank you for providing your solution—it worked brilliantly!

For anyone implementing this, keep in mind that if you’re using Bootstrap, you should replace the “hidden” class (which corresponds to display: none in Tailwind CSS) with “d-none.” Alternatively, for a more general approach, consider modifying the resize method as follows: resize() { if (!this.open && this.contentTarget.scrollHeight <= this.contentTarget.clientHeight) { this.buttonTarget.style.display = "none" } }

Also, remember that you’ll need to replace the class “line-clamp-6” with the specific class you are using in your project.

yanfroes avatar Apr 10 '24 14:04 yanfroes