-
-
Save leolabs/def58a3e89b3393b9d69820aee4afd0a to your computer and use it in GitHub Desktop.
textFit.js importable module
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Taken from https://github.com/STRML/textFit | |
const 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 | |
}; | |
export 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.getBoundingClientRect().height >= | |
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 highest best fit | |
var size = low; | |
while (low <= high) { | |
mid = (high + low) >> 1; | |
innerSpan.style.fontSize = mid + "px"; | |
var innerSpanBoundingClientRect = innerSpan.getBoundingClientRect(); | |
if ( | |
innerSpanBoundingClientRect.width <= originalWidth && | |
(settings.widthOnly || | |
innerSpanBoundingClientRect.height <= originalHeight) | |
) { | |
size = mid; | |
low = mid + 1; | |
} else { | |
high = mid - 1; | |
} | |
// await injection point | |
} | |
// found, updating font if differs: | |
if (innerSpan.style.fontSize != size + "px") | |
innerSpan.style.fontSize = size + "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.getBoundingClientRect().height - | |
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.getBoundingClientRect().width - | |
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); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment