I wrote some time ago a _javascript_ object to handle this case (requires prototype.js).
You could maybe improve it in order to get twitter's negative char count.
/**
* Counts the number of characters within a text input and displays the charcount in the
* given html element (div, span)
* field : ID of the field to monitor
* infopane : ID of the html element which will be updated with the charcount (span, div)
* limit : maximum allowed char number
* options :
* - templateString : the string that will be displayed when the maximum char number is reached.
this string can contain the #{count} parameter
* - prohibitSubmit : if true prevents the form to be submitted if the maximum char count is reached
* - onLimitExceeded : function callback called when the maximum char count is reached
* - onSubmitCancelled : function callback called before cancelling the form submission
*/
var LiveTextArea=Class.create({
initialize: function(field, infopane, limit, options){
this.field=$(field);
this.infopane=$(infopane);
this.limit=limit;
this.options=options;
if(options && options.templateString){
this.templateString=new Template(options.templateString);
}else{
this.templateString=new Template("#{count}/"+this.limit);
}
if(options && options.prohibitSubmit){
$A(this.field.ancestors()).each(function(item, index){
if(item instanceof HTMLFormElement){
Event.observe(item, 'submit', this.handleSubmit.bind(this));
return;
}
}.bind(this));
}
Event.observe(this.field, 'change', this.updateInfoPane.bind(this));
Event.observe(this.field, 'keyup', this.updateInfoPane.bind(this));
Event.observe(this.field, 'keydown', this.updateInfoPane.bind(this));
this.updateInfoPane();
},
updateInfoPane: function(event){
var chars=this.charCount();
if(chars>=this.limit && this.options && this.options.onLimitExceeded){
this.options.onLimitExceeded({charCount:chars});
}
this.infopane.update(this.templateString.evaluate({count:chars}));
},
charCount: function(){
var tmp=$F(this.field);
var lf=tmp.split('\n').length-1;
return tmp.length+lf;
},
handleSubmit: function(event){
if(this.charCount()>this.limit){
if(this.options && this.options.onSubmitCancelled){
this.options.onSubmitCancelled({charCount:this.charCount()});
}
event.stop();
return false;
}
},