sdbbs sdbbs - 7 months ago 27
HTML Question

Styling div so it is two lines of text high, with width based on text contents fit into two lines?

Consider this example:

CSS


.a1 {
border-style: solid;
border-width: 1px;
font-size: 1em;
line-height: 1.25em;
height: 2.5em;
max-height: 2.5em;
}


<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>

<body>
<h1>Hello World!</h1>

<div class="a1">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad </div>

</body>
</html>


Currently, it behaves in Firefox 43 like this upon resizing page width:

ffox-scale.gif

That is, the text attempts to fill the width of the div (which is set by width of the page).

What I want to do instead, is to:


  • Fix the height to two text lines (or as I did, 2.5em, if line-height is 1.25 em, and font-size 1em);

  • (Assuming the text is a single line) - fix the width, so the text fits in two text lines, and the width of the first line is approximately the same as the width of the second line



... that is, I would like the div to be formatted like this:

ffox-two-lines-ok

... and it would remain like this - have a fixed width/height - upon browser window scaling. And specifically - I do not want to set an explicit width for the div, but the div width should be set based on text contents:


  • if the text contents' width is smaller than say 200 px (i.e. a single line is assumed), then the div width is 200 px

  • if the text contents' width is greater than 200 px, two lines are assumed, so the text is broken up so there are approx same contents in both lines, and then the width of the longer line is takes



Is this possible to do with pure CSS - and if not, is there an approach someone could suggest with JS (or jQuery) + CSS?

Answer

Well, apparently a plain CSS solution isn't possible; and I remember seeing elsewhere on this site a JS solution that does text breaking, but I couldn't apply it to my problem at the time, so I forgot the reference - so now I rolled my own, which at least has plenty of console.log throughout.

So, here is my solution, that does exactly what I wanted:

<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <script type="text/javascript" src="/media/Data1/work/bbs/gits/econdk-vis-01_git/jquery-1.12.3.min.js"></script>
  <style type="text/css">
.a1 {
  border-style: solid;
  border-width: 1px;
  font-size: 1em;
  line-height: 1.25em;
  height: 2.5em;
  max-height: 2.5em;
}
  </style>
  <script type="text/javascript">
  // see SO: 1582534
$.fn.getWidthSingleLine = function(){
  var contents = this.contents(),
      wrapper  = '<span style="display: inline-block; white-space: nowrap;" />',
      width    = '';
  contents.wrapAll(wrapper);
  //contents.parent().css("white-space", "nowrap"); // try this, instead of setting unreasonable large width? ok - though set explicitly above
  var fontsize = parseFloat( contents.parent().css("font-size") ); //px
  width = contents.parent().width(); // parent is now the wrapper
  contents.unwrap();
  return {
    widthpx: width,
    widthem: width / fontsize
  };
}
$.fn.getHeightFromWidth = function(inwidth){
  var contents = this.contents(),
      wrapper  = '<span style="display: inline-block;" />',
      width    = '';
  contents.wrapAll(wrapper);
  contents.parent().width(inwidth); // parent is now the wrapper
  var fontsize = parseFloat( contents.parent().css("font-size") ); //px
  height = contents.parent().height(); // parent is now the wrapper
  contents.unwrap();
  return {
    heightpx: height,
    heightem: height / fontsize
  };
};

ondocready = function() {
  var retwobj = $("#txtest").getWidthSingleLine();
  console.log( retwobj.widthem + " em " + retwobj.widthpx + " px ; docw " + $(document).width() + " winw " + $(window).width() );
  var findwidthpx = retwobj.widthpx/2;
  var rethobj = $("#txtest").getHeightFromWidth( findwidthpx );
  // note: $("#txtest").css("max-height")) is in pixels!
  var maxh = parseFloat( $("#txtest").css("max-height") );
  var hdiffpx = rethobj.heightpx - maxh;
  console.log( rethobj.heightem + " em " + rethobj.heightpx + " px ; hdiffpx: " + hdiffpx + " ; findwidthpx: " + findwidthpx);
  // if we take the width of single line and halve it, we will
  // get at least two lines, maybe more - but certainly not less than two lines
  // ergo, hdiffpix should always be >= 0
  while (hdiffpx > 0) {
    // increase slowly findwidthpx; say steps of 10 px
    findwidthpx += 10;
    rethobj = $("#txtest").getHeightFromWidth( findwidthpx );
    hdiffpx = rethobj.heightpx - maxh;
    console.log(" in loop: ", rethobj.heightem + " em " + rethobj.heightpx + " px ; hdiffpx: " + hdiffpx + " ; findwidthpx: " + findwidthpx);
  }
  console.log(" after loop: ", rethobj.heightem + " em " + rethobj.heightpx + " px ; hdiffpx: " + hdiffpx + " ; findwidthpx: " + findwidthpx);
  // assign finally
  $("#txtest").width(findwidthpx);
}
$(document).ready(ondocready);
  </script>
</head>

<body>
  <h1>Hello World!</h1>

  <div id="txtest" class="a1">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad </div>

</body>
</html>

... and its expected console.log output is like this:

68.375 em 1094 px ; docw 1386 winw 1386                   
3.75 em 60 px ; hdiffpx: 20 ; findwidthpx: 547            
 in loop:  3.75 em 60 px ; hdiffpx: 20 ; findwidthpx: 557 
 in loop:  3.75 em 60 px ; hdiffpx: 20 ; findwidthpx: 567 
 in loop:  3.75 em 60 px ; hdiffpx: 20 ; findwidthpx: 577 
 in loop:  2.5 em 40 px ; hdiffpx: 0 ; findwidthpx: 587   
after loop:  2.5 em 40 px ; hdiffpx: 0 ; findwidthpx: 587

If a better answer comes along, I'll re-accept it....

Comments