Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ Functionality is identical between v1.0 and v2.0, the only change was the remova
Changelog
=========

v2.4.0
------

* Added `stopOverflow`. If there is too much text then the text will still overflow outside of the div at the smallest font size. Setting stopOverflow to true will add a `.overflow` class to the seleted div if the text is overflowing. It also adds CSS which hides the text that is overflowing in order to keep the design of the page.
* Added `maxLines`. Similar to stopOverflow but instead it adds the class if there is too many lines of text. This setting takes an input of `false` or a number of lines to apply the limit at. This needs to be improved but it's still useful to have now.
* Added `fontUnit`. Changes the unit used in the minFontSize and maxFontSize. This allows units such as cm, mm, in, pt, pc, em, vw, vh, % and rem to be used instead of px.
* Added `fontChangeSize`. This changes the amount of the font is changed by when trying to find the final font size. The default is 1 but changing this to a decimal is sometimes needed. When using rem or em font units 0.1 or 0.01 is a recommended change size.

v2.3.1
------

Expand Down Expand Up @@ -170,6 +178,9 @@ settings = {
alignHoriz: false, // if true, textFit will set text-align: center
multiLine: false, // if true, textFit will not set white-space: no-wrap
detectMultiLine: true, // disable to turn off automatic multi-line sensing
stopOverflow: false, // if true, a error we be thrown if the content is overflowing
fontUnit: 'px', // what unit should the final font be. using rems or mm is sometimes useful
fontChangeSize: 1, // how much should the font size by ajusted by each time. 0.1 and 0.01 is useful for when using a rem font unit
minFontSize: 6,
maxFontSize: 80,
reProcess: true, // if true, textFit will re-process already-fit nodes. Set to 'false' for better performance
Expand Down
2 changes: 0 additions & 2 deletions examples/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@
# Build artifacts
*.js
30 changes: 29 additions & 1 deletion examples/textFit.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@
textFit(document.getElementById('centeredMultiLine'), {alignHoriz: true, alignVert: true});
textFit(document.getElementById('maxFontSize'), {maxFontSize: 40});
textFit(document.getElementById('paddingVert'), {alignVert: true, alignHoriz: true, alignVertWithFlexbox: true});
textFit(document.getElementById('rem'), {minFontSize: 0.2, maxFontSize: 4, multiLine: true, fontUnit: 'rem', fontChangeSize: 0.01});
textFit(document.getElementById('rem2'), {minFontSize: 0.2, maxFontSize: 4, multiLine: true, fontUnit: 'rem', fontChangeSize: 0.1});
textFit(document.getElementById('percentage'), {minFontSize: 1, maxFontSize: 100, multiLine: true, fontUnit: '%'});
textFit(document.getElementById('overflow'), { minFontSize: 20, stopOverflow: true });
textFit(document.getElementById('maxLines'), { maxLines: 2, alignVert: true, });

}
function waitForTextFit() {
var interval = setInterval(function() {
Expand Down Expand Up @@ -124,7 +130,9 @@
.homeLink:hover{
color: #ACC1AC;
}

#percentage {
font-size: 4rem;
}
</style>
</head>
<body>
Expand Down Expand Up @@ -182,6 +190,26 @@ <h4>
Use Padding With Flexbox<br>
For Custom Vert Alignment!
</div>
<div id="rem" class="optionalCSS">
fontChangeSize + fontUnit<br>
rems instead of px font at a font change of 0.01<br>
</div>
<div id="rem2" class="optionalCSS">
fontChangeSize + fontUnit<br>
rems instead of px font at a font change of 0.1<br>
</div>
<div id="percentage" class="optionalCSS">
fontUnit<br>
% instead of px font<br>
</div>
<div id="overflow" class="optionalCSS">
stopOverflow: true<br>
the minFontSize is 20 but there is still overflow. the stopOverflow setting is hidding it Consequat etiam placerat ornare class, sit curae eget. Sem litora interdum mattis, etiam porta potenti ac metus sit curabitur id metus risus condimentum libero. Rutrum congue lectus quam? Leo diam tempus donec molestie dictum etiam vivamus? Velit aliquam lobortis elit urna aliquet curabitur, tempus odio rhoncus. Magna tristique felis magna euismod lorem integer a suspendisse? Tortor venenatis convallis non, ac nisi ornare. Auctor rutrum mattis malesuada tincidunt erat dictumst phasellus porta. Gravida bibendum curae? Quis rutrum! Hac nunc ultricies feugiat neque urna. Congue sagittis fusce, pretium dui praesent ligula semper platea elementum, netus id! Sodales a id nullam condimentum magna dolor vitae! Nulla primis vestibulum imperdiet proin etiam, aenean feugiat porta conubia. Imperdiet nec cras nostra vehicula felis semper convallis ornare lacinia neque etiam, erat nostra dolor lacinia, pulvinar accumsan, lacus aliquet tincidunt. Volutpat nibh aenean aliquet aenean phasellus mollis vulputate congue at elit. Etiam laoreet feugiat. Habitant ut. Ut nulla curabitur rutrum, himenaeos sapien gravida. Venenatis dictum platea iaculis! Ultricies mattis risus quisque. Metus dolor maecenas nec curabitur non. Neque viverra ac dictum. Nisi est nunc eleifend tempor suspendisse scelerisque. Primis id congue non integer! Nam amet donec lobortis, donec nostra est sociosqu! Curae tempor placerat suspendisse! Tempor aenean.<br>
</div>
<div id="maxLines" class="optionalCSS">
maxLines: 2
there is more than two lines of content here but it's only showing two
</div>
<a class="homeLink" href="http://strml.net">Created by STRML</a>
</body>
</html>
301 changes: 301 additions & 0 deletions examples/textFit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
/**
* textFit v2.3.1
* Previously known as jQuery.textFit
* 11/2014 by STRML (strml.github.com)
* MIT License
*
* To use: textFit(document.getElementById('target-div'), options);
*
* Will make the *text* content inside a container scale to fit the container
* The container is required to have a set width and height
* Uses binary search to fit text with minimal layout calls.
* Version 2.0 does not use jQuery.
*/
/*global define:true, document:true, window:true, HTMLElement:true*/

(function(root, factory) {
"use strict";

// UMD shim
if (typeof define === "function" && define.amd) {
// AMD
define([], factory);
} else if (typeof exports === "object") {
// Node/CommonJS
module.exports = factory();
} else {
// Browser
root.textFit = factory();
}

}(typeof global === "object" ? global : this, function () {
"use strict";

var defaultSettings = {
alignVert: false, // if true, textFit will align vertically using css tables
alignHoriz: false, // if true, textFit will set text-align: center
multiLine: false, // if true, textFit will not set white-space: no-wrap
stopOverflow: false, // if true, a error we be thrown if the content is overflowing
maxLines: false, // if true, textFit will throw and error if the text is over the supplied number of lines
detectMultiLine: true, // disable to turn off automatic multi-line sensing
fontUnit: 'px', // what unit should the final font be. using rems or mm is sometimes useful
fontChangeSize: 1, // how much should the font size by ajusted by each time. 0.1 and 0.01 is useful for when using a rem font unit
minFontSize: 6,
maxFontSize: 80,
reProcess: true, // if true, textFit will re-process already-fit nodes. Set to 'false' for better performance
widthOnly: false, // if true, textFit will fit text to element width, regardless of text height
alignVertWithFlexbox: false, // if true, textFit will use flexbox for vertical alignment
};

return function textFit(els, options) {

if (!options) options = {};

// Extend options.
var settings = {};
for(var key in defaultSettings){
if(options.hasOwnProperty(key)){
settings[key] = options[key];
} else {
settings[key] = defaultSettings[key];
}
}

// Convert jQuery objects into arrays
if (typeof els.toArray === "function") {
els = els.toArray();
}

// Support passing a single el
var elType = Object.prototype.toString.call(els);
if (elType !== '[object Array]' && elType !== '[object NodeList]' &&
elType !== '[object HTMLCollection]'){
els = [els];
}

// Process each el we've passed.
for(var i = 0; i < els.length; i++){
processItem(els[i], settings);
}
};

/**
* The meat. Given an el, make the text inside it fit its parent.
* @param {DOMElement} el Child el.
* @param {Object} settings Options for fit.
*/
function processItem(el, settings){
if (!isElement(el) || (!settings.reProcess && el.getAttribute('textFitted'))) {
return false;
}

// Set textFitted attribute so we know this was processed.
if(!settings.reProcess){
el.setAttribute('textFitted', 1);
}

var innerSpan, originalHeight, originalHTML, originalWidth;
var low, mid, high;

// Get element data.
originalHTML = el.innerHTML;
originalWidth = innerWidth(el);
originalHeight = innerHeight(el);

// Don't process if we can't find box dimensions
if (!originalWidth || (!settings.widthOnly && !originalHeight)) {
if(!settings.widthOnly)
throw new Error('Set a static height and width on the target element ' + el.outerHTML +
' before using textFit!');
else
throw new Error('Set a static width on the target element ' + el.outerHTML +
' before using textFit!');
}

// Add textFitted span inside this container.
if (originalHTML.indexOf('textFitted') === -1) {
innerSpan = document.createElement('span');
innerSpan.className = 'textFitted';
// Inline block ensure it takes on the size of its contents, even if they are enclosed
// in other tags like <p>
innerSpan.style['display'] = 'inline-block';
innerSpan.innerHTML = originalHTML;
el.innerHTML = '';
el.appendChild(innerSpan);
} else {
// Reprocessing.
innerSpan = el.querySelector('span.textFitted');
// Remove vertical align if we're reprocessing.
if (hasClass(innerSpan, 'textFitAlignVert')){
innerSpan.className = innerSpan.className.replace('textFitAlignVert', '');
innerSpan.style['height'] = '';
el.className.replace('textFitAlignVertFlex', '');
}
}

// Prepare & set alignment
if (settings.alignHoriz) {
el.style['text-align'] = 'center';
innerSpan.style['text-align'] = 'center';
}

// Check if this string is multiple lines
// Not guaranteed to always work if you use wonky line-heights
var multiLine = settings.multiLine;
if (settings.detectMultiLine && !multiLine &&
innerSpan.scrollHeight >= parseInt(window.getComputedStyle(innerSpan)['font-size'], 10) * 2) {
multiLine = true;
}

// If we're not treating this as a multiline string, don't let it wrap.
if (!multiLine) {
el.style['white-space'] = 'nowrap';
}
var startingSize = innerSpan.style.fontSize;

low = settings.minFontSize;
high = settings.maxFontSize;
// Binary search for highest best fit
var size = low;
while (low <= high) {
mid = parseFloat(((high + low) / 2).toFixed(2));
// mid = (high + low) >> 1;
// console.table('low: ' + low + ' mid: ', + mid + ' high: ' + high);
innerSpan.style.fontSize = mid + settings.fontUnit;
if(innerSpan.scrollWidth <= originalWidth && (settings.widthOnly || innerSpan.scrollHeight <= originalHeight)) {
size = mid;
low = mid + settings.fontChangeSize;
} else {
high = mid - settings.fontChangeSize;
}
// await injection point
}
if (startingSize !== size) {
console.log('textFit font changed size: ', size + settings.fontUnit)
}
// found, updating font if differs:
if( innerSpan.style.fontSize != size + settings.fontUnit ) innerSpan.style.fontSize = size + settings.fontUnit;

// add the required CSS in order to stop overflows
if (settings.maxLines || settings.stopOverflow) {
if (!document.getElementById("overflowStyleSheet")) {
var style = [
".overflow > span {",
"overflow: hidden;",
"}"].join("");

var css = document.createElement("style");
css.type = "text/css";
css.id = "overflowStyleSheet";
css.innerHTML = style;
document.body.appendChild(css);
}

el.classList.remove('overflow');

if (settings.maxLines) {
if (Number.isInteger(settings.maxLines)) {
const computedStyle = window.getComputedStyle(innerSpan);
// Checking if line-height is not set and has a normal value set by default
const isNormal = computedStyle.getPropertyValue('line-height') == 'normal';
// Getting the font-size of an element that will help us calculate the line-height
const fontSize = parseFloat(computedStyle.getPropertyValue('font-size'));
// If line-height is normal, we multiply the element's font-size with 1.14
if ( isNormal ) innerSpan.style.lineHeight = (fontSize * 1.14) + 'px';
const lineHeight = parseFloat(computedStyle.getPropertyValue('line-height'));
const blockHeight = innerSpan.scrollHeight;

// Setting the element's max-height
const limitHeight = (lineHeight * settings.maxLines);

innerSpan.style.maxHeight = limitHeight + 'px';

var overflow = blockHeight > limitHeight
if (overflow) {
el.classList.add('overflow')
}
} else {
console.error('TextFit maxLines needs to be given a number')
}
}

if (settings.stopOverflow) {
var overflow = innerHeight(el) < innerSpan.scrollHeight;
if (overflow) {
el.classList.add('overflow');
};
}
}

// Our height is finalized. If we are aligning vertically, set that up.
if (settings.alignVert) {
addStyleSheet();
var height = innerSpan.scrollHeight;
if (window.getComputedStyle(el)['position'] === "static"){
el.style['position'] = 'relative';
}
if (!hasClass(innerSpan, "textFitAlignVert")){
innerSpan.className = innerSpan.className + " textFitAlignVert";
}
innerSpan.style['height'] = height + "px";
if (settings.alignVertWithFlexbox && !hasClass(el, "textFitAlignVertFlex")) {
el.className = el.className + " textFitAlignVertFlex";
}
}
}

// Calculate height without padding.
function innerHeight(el){
var style = window.getComputedStyle(el, null);
return el.clientHeight -
parseInt(style.getPropertyValue('padding-top'), 10) -
parseInt(style.getPropertyValue('padding-bottom'), 10);
}

// Calculate width without padding.
function innerWidth(el){
var style = window.getComputedStyle(el, null);
return el.clientWidth -
parseInt(style.getPropertyValue('padding-left'), 10) -
parseInt(style.getPropertyValue('padding-right'), 10);
}

//Returns true if it is a DOM element
function isElement(o){
return (
typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2
o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName==="string"
);
}

function hasClass(element, cls) {
return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1;
}

// Better than a stylesheet dependency
function addStyleSheet() {
if (document.getElementById("textFitStyleSheet")) return;
var style = [
".textFitAlignVert{",
"position: absolute;",
"top: 0; right: 0; bottom: 0; left: 0;",
"margin: auto;",
"display: flex;",
"justify-content: center;",
"flex-direction: column;",
"}",
".textFitAlignVertFlex{",
"display: flex;",
"}",
".textFitAlignVertFlex .textFitAlignVert{",
"position: static;",
"}",].join("");

var css = document.createElement("style");
css.type = "text/css";
css.id = "textFitStyleSheet";
css.innerHTML = style;
document.body.appendChild(css);
}

}));
Loading