├── .gitignore ├── .npmignore ├── README.md ├── examples ├── .gitignore ├── mk-examples.sh └── textFit.html ├── index.d.ts ├── package.json ├── textFit.js ├── textFit.min.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | examples/ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `textFit` - Hassle-Free Text Fitting 2 | ================================== 3 | 4 | A **fast**, dependency-free text sizing component that quickly fits single and multi-line text to the width and/or height of its container. 5 | 6 | [Example](http://textfit.strml.net/examples/textFit.html) 7 | 8 | Capabilities 9 | ============ 10 | 11 | `textFit` is: 12 | 13 | * Fast, using binary search to quickly fit text to its container in `log n` time, making it far faster than most solutions. 14 | * Most fits are `<1ms`. See the [implementation details](#implementation-details). 15 | * Dependency-free. 16 | * Small. `4.1KB` minified and `1.5KB` gzipped. 17 | * Supports both horizontal and vertical centering, including vertical centering with Flexbox for maximum accuracy. 18 | * Supports any font face, padding, and multiline text. 19 | 20 | Browser Support 21 | =============== 22 | 23 | `textFit` supports IE9+, Firefox, Chrome, Opera, and most mobile browsers. If you find an incompatibility, 24 | please file an issue. 25 | 26 | If you require IE <= 8 support, please use the [jQuery version](https://github.com/STRML/textFit/tree/1.0-jQuery). 27 | Functionality is identical between v1.0 and v2.0, the only change was the removal of the jQuery dependency. 28 | 29 | Changelog 30 | ========= 31 | 32 | v2.3.1 33 | ------ 34 | 35 | * Fix [#20](https://github.com/STRML/textFit/issues/20) - properly iterate over `HTMLCollection` objects. 36 | 37 | v2.3.0 38 | ------ 39 | 40 | * Added `alignVertWithFlexbox`. This does better vertical alignment and fixes #14. 41 | 42 | v2.2.0 43 | ------ 44 | 45 | * Throw errors instead of just printing to console when missing height/width. 46 | - Removed `options.suppressErrors`. Wrap in `try/catch` instead if you really need this. 47 | * Slight refactor. 48 | * Added automatic build on prepublish. 49 | 50 | v2.1.1 51 | ------ 52 | 53 | * Fixed a bug with `alignVert` when reprocessing. 54 | * Added full UMD shim and published to npm. 55 | 56 | v2.1 57 | ---- 58 | 59 | * Reworked alignVert. 60 | * `reProcess` is now `true` by default. Set to `false` if you want to fire-and-forget on potentially 61 | processed nodes. This was originally false by default because it was being used in an infinite scrolling list. 62 | 63 | v2.0 64 | ---- 65 | 66 | * Removed jQuery dependency. 67 | 68 | Usage 69 | ===== 70 | 71 | ```html 72 |
140 | For illustration, these examples are run in slow motion using setInterval. Actual sizings are invisible and average <2ms. 141 |
142 |View source to see how options are applied.
143 | 144 |
117 | innerSpan.style['display'] = 'inline-block';
118 | innerSpan.innerHTML = originalHTML;
119 | el.innerHTML = '';
120 | el.appendChild(innerSpan);
121 | } else {
122 | // Reprocessing.
123 | innerSpan = el.querySelector('span.textFitted');
124 | // Remove vertical align if we're reprocessing.
125 | if (hasClass(innerSpan, 'textFitAlignVert')){
126 | innerSpan.className = innerSpan.className.replace('textFitAlignVert', '');
127 | innerSpan.style['height'] = '';
128 | el.className.replace('textFitAlignVertFlex', '');
129 | }
130 | }
131 |
132 | // Prepare & set alignment
133 | if (settings.alignHoriz) {
134 | el.style['text-align'] = 'center';
135 | innerSpan.style['text-align'] = 'center';
136 | }
137 |
138 | // Check if this string is multiple lines
139 | // Not guaranteed to always work if you use wonky line-heights
140 | var multiLine = settings.multiLine;
141 | if (settings.detectMultiLine && !multiLine &&
142 | innerSpan.getBoundingClientRect().height >= parseInt(window.getComputedStyle(innerSpan)['font-size'], 10) * 2){
143 | multiLine = true;
144 | }
145 |
146 | // If we're not treating this as a multiline string, don't let it wrap.
147 | if (!multiLine) {
148 | el.style['white-space'] = 'nowrap';
149 | }
150 |
151 | low = settings.minFontSize;
152 | high = settings.maxFontSize;
153 |
154 | // Binary search for highest best fit
155 | var size = low;
156 | while (low <= high) {
157 | mid = (high + low) >> 1;
158 | innerSpan.style.fontSize = mid + 'px';
159 | var innerSpanBoundingClientRect = innerSpan.getBoundingClientRect();
160 | if (
161 | innerSpanBoundingClientRect.width <= originalWidth
162 | && (settings.widthOnly || innerSpanBoundingClientRect.height <= originalHeight)
163 | ) {
164 | size = mid;
165 | low = mid + 1;
166 | } else {
167 | high = mid - 1;
168 | }
169 | // await injection point
170 | }
171 | // found, updating font if differs:
172 | if( innerSpan.style.fontSize != size + 'px' ) innerSpan.style.fontSize = size + 'px';
173 |
174 | // Our height is finalized. If we are aligning vertically, set that up.
175 | if (settings.alignVert) {
176 | addStyleSheet();
177 | var height = innerSpan.scrollHeight;
178 | if (window.getComputedStyle(el)['position'] === "static"){
179 | el.style['position'] = 'relative';
180 | }
181 | if (!hasClass(innerSpan, "textFitAlignVert")){
182 | innerSpan.className = innerSpan.className + " textFitAlignVert";
183 | }
184 | innerSpan.style['height'] = height + "px";
185 | if (settings.alignVertWithFlexbox && !hasClass(el, "textFitAlignVertFlex")) {
186 | el.className = el.className + " textFitAlignVertFlex";
187 | }
188 | }
189 | }
190 |
191 | // Calculate height without padding.
192 | function innerHeight(el){
193 | var style = window.getComputedStyle(el, null);
194 | return el.getBoundingClientRect().height -
195 | parseInt(style.getPropertyValue('padding-top'), 10) -
196 | parseInt(style.getPropertyValue('padding-bottom'), 10);
197 | }
198 |
199 | // Calculate width without padding.
200 | function innerWidth(el){
201 | var style = window.getComputedStyle(el, null);
202 | return el.getBoundingClientRect().width -
203 | parseInt(style.getPropertyValue('padding-left'), 10) -
204 | parseInt(style.getPropertyValue('padding-right'), 10);
205 | }
206 |
207 | //Returns true if it is a DOM element
208 | function isElement(o){
209 | return (
210 | typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2
211 | o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName==="string"
212 | );
213 | }
214 |
215 | function hasClass(element, cls) {
216 | return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1;
217 | }
218 |
219 | // Better than a stylesheet dependency
220 | function addStyleSheet() {
221 | if (document.getElementById("textFitStyleSheet")) return;
222 | var style = [
223 | ".textFitAlignVert{",
224 | "position: absolute;",
225 | "top: 0; right: 0; bottom: 0; left: 0;",
226 | "margin: auto;",
227 | "display: flex;",
228 | "justify-content: center;",
229 | "flex-direction: column;",
230 | "}",
231 | ".textFitAlignVertFlex{",
232 | "display: flex;",
233 | "}",
234 | ".textFitAlignVertFlex .textFitAlignVert{",
235 | "position: static;",
236 | "}",].join("");
237 |
238 | var css = document.createElement("style");
239 | css.type = "text/css";
240 | css.id = "textFitStyleSheet";
241 | css.innerHTML = style;
242 | document.body.appendChild(css);
243 | }
244 | }));
245 |
--------------------------------------------------------------------------------
/textFit.min.js:
--------------------------------------------------------------------------------
1 | (function(root,factory){"use strict";if(typeof define==="function"&&define.amd){define([],factory)}else if(typeof exports==="object"){module.exports=factory()}else{root.textFit=factory()}})(typeof global==="object"?global:this,function(){"use strict";var defaultSettings={alignVert:false,alignHoriz:false,multiLine:false,detectMultiLine:true,minFontSize:6,maxFontSize:80,reProcess:true,widthOnly:false,alignVertWithFlexbox:false};return function textFit(els,options){if(!options)options={};var settings={};for(var key in defaultSettings){if(options.hasOwnProperty(key)){settings[key]=options[key]}else{settings[key]=defaultSettings[key]}}if(typeof els.toArray==="function"){els=els.toArray()}var elType=Object.prototype.toString.call(els);if(elType!=="[object Array]"&&elType!=="[object NodeList]"&&elType!=="[object HTMLCollection]"){els=[els]}for(var i=0;i