Guessing character width in jQueryPosted on: November 09, 2012
I recently updated superLabels in response to a pull request requesting a way to be able to still display the label until after a certain number of characters has been typed.
I thought this would be a quick and easy task, until I realised that what I immediately had in mind had to do with font-size and line-height. This obviously has to do with the vertical size of the font, and not the horizontal size. A further complication to this comes with the fact that not all fonts are mono-spaced so an 'l' isn't as wide as an 'm' (for example).
What you'll see below is the solution I came up with (and is the one you will find used in superLabels). Note: I originally came up with this on my own, but as is the nature of the web, it turns out that it's not an entirely new thing.
I like to think that I commented the code well enough for me to not have to verbosely explain it, so have a gander at the goods:
_approximateChars = function (_field, _label) {
var _available,
_charLen,
_chars = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
(_properties = [
"font-family",
"font-size",
"font-weight",
"letter-spacing",
"line-height",
"text-shadow",
"text-transform",
]),
(_tmp = $("<div>" + _chars + "</div>"));
// Loop through each of the defined properties so that we can get the font looking the same size.
// I know this isn't too great for performance, but for now I don't know of a better way to do this.
// If you do know of a better way, please hit me with a pull request.
$.each(_properties, function (i, _prop) {
_tmp.css(_prop, _field.css(_prop));
});
_tmp.css({
position: "absolute", // so it's out of the document flow.
visibility: "hidden", // so that it's not visible, but still takes up space in the DOM so we can grab the width
});
// Append this to the parent so that it can correctly replicate the style of the field.
_field.parent().append(_tmp);
// Get the average length *per character*
_charLen = Math.round(_tmp.width() / _chars.length);
// Remove our temporary div from the DOM.
_tmp.remove();
// Figure out how much room we have to work with here.
_available = _field.width() - _label.width();
// Set the data-sl-char-limit attribute for this field to our approximated value.
_field.data("slCharLimit", Math.floor(_available / _charLen));
};
... and that's it. Feel free to drop me a comment, or even better, fork superLabels and send a pull request on github if you can think of a better way for anything that I did inefficiently.