Skip to content

Instantly share code, notes, and snippets.

@JawsomeJason
Last active September 25, 2016 09:16
Show Gist options
  • Save JawsomeJason/6458615 to your computer and use it in GitHub Desktop.
Save JawsomeJason/6458615 to your computer and use it in GitHub Desktop.
Edge to Edge, a jQuery plugin that helps fit text to the edges of its container.

jQuery Edge-to-Edge Plugin

It finds the optimum font size to fit a certain container width. Coolest thing: It does this by shrinking/growing the font by half of the last attempt until it finds the sweet spot [ O(log n): 154px font = max 8 checks ]. There might be a slight visual offset between two headers one on top of the other. This is due to no checking on letter-spacing. That might incur a greater performance hit, as we'd be getting into floating-point pixels.

A Pen by Jason Featheringham on CodePen.

License.

<header>
<hgroup>
<h1>Extra! Extra!</h1>
<h2>Read All About It!</h2>
</hgroup>
</header>
<h2><span>Extra! Extra!</span><span>Read All About It!</span></h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed urna leo, hendrerit et ligula pharetra, placerat lobortis justo. Pellentesque sit amet aliquam ante, ac facilisis lectus. Suspendisse porta tellus id diam venenatis tempor. Sed nec leo consectetur, iaculis magna lobortis, porttitor risus. Mauris nulla velit, auctor vel magna a, tempus rhoncus neque. Pellentesque eu tellus lacinia, viverra felis eget, ornare risus. Ut non suscipit tortor, eu luctus enim. Phasellus tincidunt sem lacus, nec blandit dui luctus quis. Duis magna nunc, luctus ac pellentesque ut, tristique ac massa. Cras condimentum quam eget pellentesque malesuada. Maecenas nec turpis dolor. Fusce vitae vulputate dolor, nec sollicitudin leo. Nunc sollicitudin cursus arcu et feugiat.</p>
<blockquote>
<span>In pellentesque scelerisque</span>
<span>quam, vitae malesuada justo</span>
</blockquote>
<p>Vestibulum pulvinar fermentum malesuada. Ut in venenatis quam. Aliquam eget dignissim lectus, non iaculis eros. Pellentesque et leo eu velit sollicitudin varius. Maecenas porttitor, tellus ac euismod semper, diam eros adipiscing magna, eu tincidunt lorem elit sed lacus. Nulla adipiscing bibendum ipsum eu dignissim. Aliquam rhoncus turpis at quam viverra, non semper quam tincidunt. In pellentesque scelerisque quam, vitae malesuada justo. Nullam ac lacinia lectus, quis fermentum risus. Nunc consequat tellus at metus molestie aliquam. Nunc fringilla tincidunt pulvinar. Proin imperdiet at sem ac varius. Morbi non sapien in ligula pulvinar sodales. Pellentesque porta id arcu ut laoreet.</p>
/**
* Increases/decreases font of element so that it fits on one line
* @returns {jQuery} original jQuery object
*/
$.fn.edgetoedge = function() {
return this.each( function( ) {
var $element = $( this ),
display = $element.css('display'),
// easy way to get height metrics
// NOTE: this will trigger reflow!!! Use sparingly.
metrics = function() {
var height = $element.height(),
// get target height as if container was wide enough to fit it all
lineHeight = $element.css('white-space', 'nowrap' ).height();
// put font-wrapping back to normal
$element.css( 'white-space', 'normal' );
return {
height: height,
lineHeight: lineHeight
};
},
// NOTE: this will trigger reflow!!! Use sparingly.
resize = function( size ) {
$element.css('font-size', size + 'px');
},
stats, delta, dir, growths = 0;
// element must be block-level to avoid inline spacing issues
if( display !== 'block' ) {
$element.css( 'display', 'block' );
}
// if there are child elements, process those instead
var $children = $element.find( '*' );
if( $children.length ) {
$children.edgetoedge();
return;
}
// EXTRA: exit if no text inside
if( !$element.text() ) {
console.log( 'no text' );
return;
}
// increase font size until we are wrapping at least one line
while( ( stats = metrics() ) && stats.height === stats.lineHeight ) {
growths++;
resize( parseInt( $element.css('font-size' ) ) * 2 );
};
// this will be our basis for checking
var overflowingFontSize = parseInt( $element.css('font-size' ) );
// for debugging
var runs = 0, original = overflowingFontSize;
// use logarithmic reduction to determine best font size for one line
while( true ) {
stats = metrics();
// +/- font size by half of it's last change.
delta = ( delta || overflowingFontSize ) / 2;
// once the delta is too low, stop.
if( delta < 1 ) {
// sometimes, the last change is 1px over instead of under. Compensate.
if( stats.height > stats.lineHeight ) {
runs++;
resize( parseInt( $element.css( 'font-size' ) ) - 1 );
}
break;
}
runs++;
// -1 for decrease, 1 for increase. Decrease if equal
dir = stats.height > stats.lineHeight && -1 || 1;
// increase or decrease font size by half of last increase/decrease.
// EXTRA: Round number down for browser consistency.
resize( Math.floor( overflowingFontSize += delta * dir ) );
};
$element.css('white-space', 'normal');
//*
// for debugging
var finalFontSize = parseInt( $element.css('font-size' ) ),
expectedRuns = Math.ceil( Math.log( finalFontSize ) / Math.log( 2 ) );
console.log( {
'element': $element,
'original font size': original,
'new font size': finalFontSize,
'expected runs': expectedRuns + ' - ' + ( expectedRuns + 1 ),
'actual runs': runs
} );
//*/
} );
};
$( function() {
$( 'header, blockquote' ).edgetoedge();
} );
@import "compass";
body {
margin: 1em;
text-align: justify;
line-height: 1.4;
font-family: serif;
}
h1,h2, p {
margin: 0 0 1rem;
}
h1,h2 {
line-height: 1;
}
blockquote {
width: 33%;
min-width: 200px;
float: right;
margin: 0 0 .5em .5em;
border: 1px solid #ccc;
padding: 0 0 .5em .5em;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment