Sunday, August 10, 2008

Javascript: Warn If Overflow

I recently came across a requirement to provide a warning to the user if the text entered in a text box exceeds the display area in a DIV, and found and old script I cooked up for it. An issue with setting the maximum length for the text box is that the 'W' and 'i' characters don't occupy the same space width variable-width fonts. I though it would be possible to measure the width of the text when the text is placed within a container using the offsetWidth property and I wrote up a little function:

function getSize() {
var divNitinz = document.getElementById('divNitinz');
var txtName = document.getElementById('txtName');
divNitinz.style.display = 'inline';
divNitinz.innerHTML = txtName.value;
var txtWidth = divNitinz.offsetWidth;
divNitinz.style.display = 'none';
return txtWidth;
}


I then created another function to get this value and compare it with the DIV in which I wanted to display the text and that looks like this:

function checkSize() {
var divDisplay = document.getElementById('divDisplay');
var txtName = document.getElementById('txtName');
if (divDisplay.offsetWidth <>
alert('Text overflow');
divDisplay.innerHTML = 'Text width: ' + getSize() + ' Display width: ' + divDisplay.offsetWidth;
} else {
divDisplay.innerHTML = txtName.value;
window.status = 'Text width: ' + getSize() + ' Display width: ' + divDisplay.offsetWidth;
}
}


My HTML is pretty simple:

<form>

<input type="text" name="txtName" id="txtName" onkeyup="checkSize();" />

<input type="text" name="txtName" id="txtName" onkeyup="checkSize();" />


<span id="divNitinz" style="display: none;"></span>
<div id="divDisplay" style="width: 120px; background-color: #FFFF99;"></div>
<input type="button" onclick="checkSize();" value="check" name="btnCheck" />
</form>

You've probably noticed the setTimeout that I've defined on the onkeydown event. I use it to get the text after the key has taken effect. I tried the onkeypress event before using onkeydown and the difference is the keypress event doesn't occur when you press backspace while the keydown event does, which makes it the better of the two.

If you've got any enhancements to this script or a better way to do detect overflow, let me know and I'll post it up here.

4 comments:

Joshua Paine said...

onkeyup is the event you want that fires for every key press and after which you don't have to setTimeout to get the new value of the field.

Anonymous said...

Are you sure you want to set innerHTML to unsanitized text content? What if someone puts angle brackets in there? You could even open yourself up to XSS attacks.

Nitin Reddy Katkam said...

@phyzome

Thanks for your comment.

I agree - innerHTML does pose a security risk. The first thing I do whenever I see a CMS that allows HTML input is to try and insert a script.

For this example, I would've used innerText instead but that's IE-specific and has the textContent equivalent in Firefox. Instead of adding more code for determining which browser the user is running, I thought it would be best to just use innerHTML, which is common to both Firefox & IE, to make the example simpler.

Nitin Reddy Katkam said...

@midnightmonster

Thanks for the tip - I'll make sure I incorporate it into future examples.

I haven't been doing much Javascript-ing lately so it took me a while to figure out which key event to use. On looking at the event names, onkeypress seemed like the appropriate one but I then realised it doesn't fire on backspace. I then went for onkeydown and the rest is history.

I'll add that to the post so other readers go with onkeyup instead of onkeydown.