Back in 2008 the standard way to limit a textarea was a clipboard-ready script from Dynamic Drive: keypress handlers, document.all sniffing, eval(), the works. It worked, but today you rarely need any of it — HTML has a native attribute for exactly this.
The native way: maxlength
<textarea> (unlike old IE) fully supports the maxlength attribute. The browser silently stops accepting characters past the limit:
<textarea maxlength="200" rows="5" cols="40"></textarea>
The one caveat: maxlength counts JavaScript string units (UTF-16 code units), not user-perceived characters. Emoji and some symbols count as 2. For most inputs this doesn’t matter; for names and social text it can.
Showing a live “characters remaining” counter
The 2008 script existed mostly for the counter, not the limit. That part still needs JavaScript, but it’s a few lines with no browser sniffing:
<label> Bio (<span id="remaining">200</span> characters remaining) </label> <textarea id="bio" maxlength="200" rows="5" cols="40"></textarea> <script> const bio = document.getElementById('bio'); const remaining = document.getElementById('remaining'); function updateCount() { remaining.textContent = bio.maxLength - bio.value.length; } bio.addEventListener('input', updateCount); updateCount(); </script>
The input event fires on typing, pasting, drag-and-drop and autofill alike — no keypress/keyup pairs needed. Note the counter doesn’t enforce anything by itself; it just mirrors what maxlength already enforces.
Enforce it on the server too
Client-side limits are a convenience, not a security control — a crafted request skips the browser entirely. Validate the length server-side in whatever stack you use (e.g. strlen() / mb_strlen() in PHP, [MaxLength] in ASP.NET, a schema check in Node). If you need grapheme-accurate counting (emoji, combining characters), use Intl.Segmenter in JS or graphemes-aware functions server-side.
Seventeen years of “progress” here is mostly deletion: the 100-line sniffer script became one attribute plus an optional five-line counter.