# Responsive text by JavaScript

I'm working on a JavaScript plugin that makes font-sizes flexible (for a responsive layout). The code works well so far, but I'm sure that it's capable of improvement (especially if you look at the global variable).

(function () {
var extend = function (obj, ext) {
for (var key in ext) {
if (ext.hasOwnProperty(key)) {
obj[key] = ext[key];
}
}
return obj;
};

window.txt = function (el, k, c) {
var s = extend({
n: Number.NEGATIVE_INFINITY,
x: Number.POSITIVE_INFINITY
}, c);

var fit = function () {
var a = k;

var i = function () {
el.style.fontSize = Math.max(Math.min(el.clientWidth / a, parseFloat(s.x)), parseFloat(s.n)) + "px";
};
i();
};

if (el.length) {
for (var i = 0; i < el.length; i++) {
fit(el[i]);
}
} else {
fit(el);
}

return el;
};
})();


DEMO

Meaning of the used variables:

• n = minFontSize
• x = maxFontSize
• s = settings
• c = config
• el = element
• i → running the function
• a or k = fontRatio/compressor

window.txt(document.getElementById("title"), 10);

executes the JavaScript Plugin on an element.

Thoughts:

1. Variable names: I hate typing unnecessary text as much as the next dev, but variable names are not a good place to skimp. It's one thing when using i as an index variable, for example, but for others, please, please, please use meaningful names. If you have to provide a key after the fact, this should be a good clue that you've not adequately done so.

2. Don't pollute the global space. If the caller needs access to the function, return it instead. Or use modules (module.exports or the like).

3. minFontSize less than zero doesn't make much sense. I'd cap it at zero rather than –∞.

4. maxFontSize being capped at +∞ probably doesn't make sense either.

5. Your code will re-parse the minimum and maximum font sizes each time the handler is called. Cache these instead.

6. var a = k: Why? In this case, just use k instead.

7. i() does not do what you think it will: if el is an array, el.style is going to be undefined, and el.style.fontSize will error.

8. When iterating arrays using for (;;;), cache the length, like so:

for (let i=0, l=arr.length;i<l;i++) { ... } // for ES5 use "var"

9. If k = 0, what happens? Division by zero...

This is a very quick rendition of your code (in ES6), with meaningful variable names, and implementing caching and such, but it might be useful:

function fitText(el, ratio, {minFontSize, maxFontSize}) {
el.style.fontSize = Math.max(Math.min(el.clientWidth / ratio, maxFontSize), minFontSize) + "px";
}

function attachListeners(el, ratio, {minFontSize, maxFontSize} = {}) {
const boundFitText = fitText.bind(undefined, el, ratio, {minFontSize, maxFontSize}),
}

export function adjustTextSize(el = [], ratio = 1,
{
minFontSize=0,
maxFontSize=Number.POSITIVE_INFINITY
} = {})
{
if (ratio === 0) {
return el; // prevent division by zero later; throwing an exception might be better here, though
}

const parsedOptions = {
minFontSize: parseFloat(minFontSize),
maxFontSize: parseFloat(maxFontSize)
}

const els = (el instanceof Array) ? el : [el];
for (let i=0, l=els.length; i<l; i++) {
let actualEl = els[i];
fitText(actualEl, ratio, parsedOptions);
attachListeners(actualEl, ratio, parsedOptions);
}
return el;
};

• Thanks for the effort! I know that the script should just give me a preview, but could you edit the JS (jsbin.com/gojusebeja/1/edit?html,js,output) to make it run? Mar 8 '15 at 11:11
• Hi! See jsbin.com/zobacoriso/2/edit?html,js,output. This is automatically generated from ES6 code using Babel's transpiler, so it's not quite as pretty as hand-written (the _ref and _ref\$... aren't so great), but it should give you a good idea. Mar 9 '15 at 0:29
• Thank you. Last remark: I tested the original JS with your enhancement by running benchmarks and according to jsPerf the original is 60% "faster". I'm in a conflict now cause I don't know if I should go for speed or quality. Mar 10 '15 at 19:26
• Hi there -- jsPerf is great for a lot of things, but always take the results with a large grain of salt. For example, I can get your version to be slower than mine simply by reversing the test order. In fact, if I duplicate your version (and the same would be true of mine), the duplicate test runs much slower. I'll also argue that I'm not sure the perf here is really testing what you want. More likely than not, you're getting slower behavior simply because you are attaching more event listeners through each loop, something you wouldn't normally do. Mar 10 '15 at 19:56
• That's not to say you don't want the initial setup to be fast (you do), but you also need to ensure that the event handlers are fast as well (and that's probably the more likely scenario, right?). Your jsPerf doesn't test that at all. And of course, the browser becomes really unhappy by the end of the test because there are so many event listeners sitting out there. The more you add, the slower things become. Mar 10 '15 at 20:04

something that I would definitely do on the next iteration, before this code gets too big, is to make the variables meaningful. I can't take a quick look at this and tell you that I know what it is doing or how to plug into it.

JavaScript is an older browser language that, for a long time, required using as little code as possible to achieve it's goal, this is not the case anymore.

in a huge JavaScript file you can use minify to make your code more compact and take out all the extra spaces. But please don't skimp on the variable names, it makes it hard for others to see exactly what your code is doing.