/** * 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 detectMultiLine: true, // disable to turn off automatic multi-line sensing 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
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'; } low = settings.minFontSize; high = settings.maxFontSize; // Binary search for best fit var ans = low; while (low <= high) { mid = parseInt((low + high) / 2, 10); innerSpan.style.fontSize = mid + 'px'; if(innerSpan.scrollWidth <= originalWidth && (settings.widthOnly || innerSpan.scrollHeight <= originalHeight)){ low = mid + 1; ans = mid; } else { high = mid - 1; } } // Sub 1 at the very end, this is closer to what we wanted. innerSpan.style.fontSize = ans + 'px'; // 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); } }));