0

I have seen a few solutions using text-overflow:ellipsis to show a few lines of a text and end with "..." if it doesn't fit. However, I am looking for a CSS or JS solution where you would have a fixed height (or number of lines) and the width of the container naturally adapts to show the full text content.

If necessary, I can make a few images to explain better what I need.

Hopefully a solution exists !

Gyoo
  • 209
  • 3
  • 15
  • https://stackoverflow.com/questions/454202/creating-a-textarea-with-auto-resize – j08691 Jun 05 '17 at 20:47
  • Possible duplicate https://stackoverflow.com/questions/33058004/applying-an-ellipsis-to-multiline-text – Asons Jun 05 '17 at 20:57
  • @LGSon, he's not looking for a solution for trimming the text. At Gyoo, this is not possible using CSS and, even in JavaScript, the description you provided is not enough. You probably want more than just a number of lines. I'll try to provide a `JS` solution, just for fun. – tao Jun 05 '17 at 22:12
  • @AndreiGheorghiu After reread the question, you are most likely correct, that OP doesn't look for what I suggested as a dupe. – Asons Jun 05 '17 at 22:28

1 Answers1

0

This would be a rough form.

let quotes = [
      'I do not regret one professional enemy I have made. Any actor who doesn\'t dare to make an enemy should get out of the business.',
      'A man thinks that by mouthing hard words he understands hard things.',
      'To say that a man is made up of certain chemical elements is a satisfactory description only for those who intend to use him as a fertilizer.',
      'Security is a kind of death.',
      'The worst loneliness is not to be comfortable with yourself.',
      'The fact is, sometimes it\'s hard to walk in a single woman\'s shoes. That\'s why we need really special ones now and then to make the walk a little more fun.',
      'Behind the phony tinsel of Hollywood lies the real tinsel.'
    ],
    rows = 3,
    quote, lastQuote,
    $quote = $('.quote'),
    getDifferentQuote = function() {
      quote = quotes[Math.floor(Math.random() * quotes.length)];
      if (quote === lastQuote) {
        return getDifferentQuote();
      } else {
        lastQuote = quote;
        return quote;
      }
    },
    changeQuote = function() {
      quote = getDifferentQuote();
      let item = $('<span />', {
        text : quote,
        class: 'tester'
      });
      item.appendTo($('body'));
      let desiredHeight = item.height() * rows;
      item.css({
        whiteSpace: 'normal',
        width     : 30 + 'px'
      });
  
      while (item.height() > desiredHeight) {
        item.css({width: (item.width() + 1) + 'px'});
      }
      $quote.css({width: item.width() + 'px'});
      $quote.text(item.text());
      item.remove();
    };
$('button').on('click', changeQuote);
$(window).load(changeQuote);
.holder {
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 30px;
  background-color: #f5f5f5;
  min-height: 100px;
}
.quote {
  text-align: center;
}
.tester {
  position: absolute;
  white-space: nowrap;
  display: block;
  opacity: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="holder">
  <div class="quote"></div>
</div>
<button>Change quote</button>
  • selecting a random quote, appending it as an invisible item to the body, initially having 1 row,
  • calculate desired height by multiplying its height with nr. of rows (3 in this example),
  • make it very narrow and while its height is bigger than the desired one, keep increasing width.
  • when at desired height, copy its contents to the element in the "panel" and delete invisible item.

Of course, a pre-requisite would be to make sure the invisible item has same line-height, font-size and letter-spacing as the one I'm going to use for display, which is true in my case because I haven't styled any of them. Also, if the display item has padding, the invisible one needs the same or you should account for it when calculating.

Hackish, but it works. Questions?

As a side-note, what I described above is inside the changeQuote() function. The rest are just helpers (adding quotes to have texts with various lenghts, a function that makes sure the quotes don't get repeated and other details - you don't need most of this stuff).

And another note: if you're here to learn, you'll probably find what I made useful. If you're here to get a job done, you're on the wrong website, IMHO. The proper way to do it would be to hire a professional.


To understand why I'm doing this using an invisible item: even though it's quite fast (faster than I was expecting), if I did it on a visible item theoretically it could cause glimpses of its intermediary states to be rendered. Let's make a test, with the invisible item visible:

let quotes = [
      'I do not regret one professional enemy I have made. Any actor who doesn\'t dare to make an enemy should get out of the business.',
      'A man thinks that by mouthing hard words he understands hard things.',
      'To say that a man is made up of certain chemical elements is a satisfactory description only for those who intend to use him as a fertilizer.',
      'Security is a kind of death.',
      'The worst loneliness is not to be comfortable with yourself.',
      'The fact is, sometimes it\'s hard to walk in a single woman\'s shoes. That\'s why we need really special ones now and then to make the walk a little more fun.',
      'Behind the phony tinsel of Hollywood lies the real tinsel.'
    ],
    rows = 3,
    quote, lastQuote,
    $quote = $('.quote'),
    getDifferentQuote = function() {
      quote = quotes[Math.floor(Math.random() * quotes.length)];
      if (quote === lastQuote) {
        return getDifferentQuote();
      } else {
        lastQuote = quote;
        return quote;
      }
    },
    changeQuote = function() {
      quote = getDifferentQuote();
      let item = $('<span />', {
        text : quote,
        class: 'tester'
      });
      item.appendTo($('body'));
      let desiredHeight = item.height() * rows;
      item.css({
        whiteSpace: 'normal',
        width     : 30 + 'px'
      });
  
      while (item.height() > desiredHeight) {
        item.css({width: (item.width() + 1) + 'px'});
      }
      $quote.css({width: item.width() + 'px'});
      $quote.text(item.text());
      item.remove();
    };
$('button').on('click', changeQuote);
$(window).load(changeQuote);
.holder {
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 30px;
  background-color: #f5f5f5;
  min-height: 100px;
}
.quote {
  text-align: center;
}
.tester {
  position: absolute;
  white-space: nowrap;
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="holder">
  <div class="quote"></div>
</div>
<button>Change quote</button>

It never shows up. It should appear under the button but it's never rendered. At least not on my machine. Pretty cool.

tao
  • 82,996
  • 16
  • 114
  • 150
  • Thank you for your very complete reply :) To reply to what you said, I'm here for both : I want to learn, as I'm not a CSS pro yet, and this is only for a personal project so hiring a professional doesn't seem worth. I will try to test your solution ASAP, but that indeed seems to be a good work base. I will probably tune it a bit to have a minimum line width but that's my own problem :) – Gyoo Jun 06 '17 at 05:53