Squared Text

Making a block of text equal in height and width.

Text, like water, flows. It does not fit into containers of a layout’s convenience. Rather, you must build containers to fit it.

I want a square container, so to comprimize with the flow, I am willing to adjust the size of the container so long as it remains square.

The Development Gist

  • Get the area of the existing container by multiplying the width and height.
  • We take the square root of this area. This gives us our initial dimensions, which here we apply to the width only. Unfortunately, text-wrapping rears its hyphenated head. Due to word breaks, the actual height of the container will be taller than our square root. Forcing the square height is undesirable as it will create overflows. Boo hiss.
  • We take an average of the new width with the new height. This number is much closer to our square; this we apply to both the height and width.
  • Next, we’re going to align the text into the middle of the box. Short text — particularly text that doesn’t wrap, benefits the most here. We wrap the contents of the container with a span set to display:table-cell; vertical-align:middle;.
  • Lastly, we’re going to compare the height and width of our new span wrapper. If there’s a difference, we have an orphan — one last word in the paragraph that wrapped. This little rabblerouser throws off the layout, so we run $.fn.square() on the parent container again — that should even out the box size and return the orphan to the fold.

Other Thoughts

I don’t love the repaints here. Each block takes at least two (more if there are orphans) repaints. And repaints take browser resources.

If using a font-face declaration, you’ll need to ensure the font is loaded before you start your calculations. Changing character dimensions will throw everything off.

In which this:

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 pokery nonens.

Becomes this!

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 pokery nonens.

Le code:

$.fn.square = function(){ return this.each(function(i,e){ var what = $(e) , textArray = what.text().split(' ') , minWidth , avg ; /*Get longest word in string by forcing each word onto a line of it’s own, then measure width of container.*/ $('
').html(textArray.join('
')).css({ fontSize:what.css('font-size'), height:0, overflow:'hidden', position:'absolute' }).appendTo('body').each(function(i,e){ var x = $(e); minWidth = x.width(); x.remove(); }); /*Resize 1 of 2*/ what.css({ height : 'auto', width : Math.sqrt(what.width() * what.height()) }); /*Resize 2 of 2 — refining*/ /*Making a square does not account for text-wrapping — the width above does not match height. For a more even square, we average the new dimensions. This result is pretty close.*/ /*Last, we wrap the contents in a vertically-aligned table cell, just for a last bit of balance.*/ /*Applying the greater of calculated width or the longest word width.*/ avg = Math.max(Math.ceil((what.width() + what.height()) / 2), minWidth); what.css({ height : avg, width : avg }) .wrapInner('').find('span').css({ display:'table-cell', verticalAlign:'middle', height:$(this).width() }).each(function(i,e){ var span = $(e); if(span.height() - span.width() > 2){ /*Orphans! Take evasive action! Run $.fn.square() again, the new dimensions should smooth orphans over.*/ span.parent().html(span.html()).square(); } }); }); }