How do I detect change event on textarea using javascript?
I'm trying to detect how many characters left is available as you type.
I tried using the onchange event, but that seems to only kick in when focus is out.
The best way to do this, cross-browser: use a combination of the input and onpropertychange events, like so:
var area = container.querySelector('textarea');
if (area.addEventListener) {
area.addEventListener('input', function() {
// event handling code for sane browsers
}, false);
} else if (area.attachEvent) {
area.attachEvent('onpropertychange', function() {
// IE-specific event handling code
});
}
The input event takes care of IE9+, FF, Chrome, Opera and Safari, and onpropertychange takes care of IE8 (it also works with IE6 and 7, but there are some bugs).
The advantage of using input and onpropertychange is that they don't fire unnecessarily (like when pressing the Ctrl or Shift keys); so if you wish to run a relatively expensive operation when the textarea contents change, this is the way to go.
Now IE, as always, does a half-assed job of supporting this: neither input nor onpropertychange fires in IE when characters are deleted from the textarea. So if you need to handle deletion of characters in IE, use keypress (as opposed to using keyup / keydown, because they fire only once even if the user presses and holds a key down).
Source: http://www.alistapart.com/articles/expanding-text-areas-made-elegant/
EDIT: It seems even the above solution is not perfect, as rightly pointed out in the comments: the presence of the addEventListener property on the textarea does not imply you're working with a sane browser; similarly the presence of the attachEvent property does not imply IE. If you want your code to be really air-tight, you should consider changing that. See Tim Down's comment for pointers.
You will need to use onkeyup and onchange for this. The onchange will prevent context-menu pasting, and the onkeyup will fire for every keystroke.
See my answer on How to impose maxlength on textArea for a code sample.
For Google-Chrome, oninput will be sufficient (Tested on Windows 7 with Version 22.0.1229.94 m).
For IE 9, oninput will catch everything except cut via contextmenu and backspace.
For IE 8, onpropertychange is required to catch pasting in addition to oninput.
For IE 9 + 8, onkeyup is required to catch backspace.
For IE 9 + 8, onmousemove is the only way I found to catch cutting via contextmenu
Not tested on Firefox.
var isIE = /*#cc_on!#*/false; // Note: This line breaks closure compiler...
function SuperDuperFunction() {
// DoSomething
}
function SuperDuperFunctionBecauseMicrosoftMakesIEsuckIntentionally() {
if(isIE) // For Chrome, oninput works as expected
SuperDuperFunction();
}
<textarea id="taSource"
class="taSplitted"
rows="4"
cols="50"
oninput="SuperDuperFunction();"
onpropertychange="SuperDuperFunctionBecauseMicrosoftMakesIEsuckIntentionally();"
onmousemove="SuperDuperFunctionBecauseMicrosoftMakesIEsuckIntentionally();"
onkeyup="SuperDuperFunctionBecauseMicrosoftMakesIEsuckIntentionally();">
Test
</textarea>
I know this isn't exactly your question but I thought this might be useful.
For certain applications it is nice to have the change function fire not every single time a key is pressed. This can be achieved with something like this:
var text = document.createElement('textarea');
text.rows = 10;
text.cols = 40;
document.body.appendChild(text);
text.onkeyup = function(){
var callcount = 0;
var action = function(){
alert('changed');
}
var delayAction = function(action, time){
var expectcallcount = callcount;
var delay = function(){
if(callcount == expectcallcount){
action();
}
}
setTimeout(delay, time);
}
return function(eventtrigger){
++callcount;
delayAction(action, 1200);
}
}();
This works by testing if a more recent event has fired within a certain delay period. Good luck!
I know this question was specific to JavaScript, however, there seems to be no good, clean way to ALWAYS detect when a textarea changes in all current browsers. I've learned jquery has taken care of it for us. It even handles contextual menu changes to text areas. The same syntax is used regardless of input type.
$('div.lawyerList').on('change','textarea',function(){
// Change occurred so count chars...
});
or
$('textarea').on('change',function(){
// Change occurred so count chars...
});
You can listen to event on change of textarea and do the changes as per you want. Here is one example.
const textArea = document.getElementById('my_text_area');
textArea.addEventListener('input', () => {
var textLn = textArea.value.length;
if(textLn >= 100) {
textArea.style.fontSize = '10pt';
}
})
<html>
<textarea id='my_text_area' rows="4" cols="50" style="font-size:40pt">
This text will change font after 100.
</textarea>
</html>
Keyup should suffice if paired with HTML5 input validation/pattern attribute. So, create a pattern (regex) to validate the input and act upon the .checkValidity() status. Something like below could work. In your case you would want a regex to match length. My solution is in use / demo-able online here.
<input type="text" pattern="[a-zA-Z]+" id="my-input">
var myInput = document.getElementById = "my-input";
myInput.addEventListener("keyup", function(){
if(!this.checkValidity() || !this.value){
submitButton.disabled = true;
} else {
submitButton.disabled = false;
}
});
Code I have used for IE 11 without jquery and just for a single textarea:
Javascript:
// Impede que o comentário tenha mais de num_max caracteres
var internalChange= 0; // important, prevent reenter
function limit_char(max)
{
if (internalChange == 1)
{
internalChange= 0;
return;
}
internalChange= 1;
// <form> and <textarea> are the ID's of your form and textarea objects
<form>.<textarea>.value= <form>.<textarea>.value.substring(0,max);
}
and html:
<TEXTAREA onpropertychange='limit_char(5)' ...
Try this one. It's simple, and since it's 2016 I am sure it will work on most browsers.
<textarea id="text" cols="50" rows="5" onkeyup="check()" maxlength="15"></textarea>
<div><span id="spn"></span> characters left</div>
function check(){
var string = document.getElementById("url").value
var left = 15 - string.length;
document.getElementById("spn").innerHTML = left;
}
The best thing that you can do is to set a function to be called on a given amount of time and this function to check the contents of your textarea.
self.setInterval('checkTextAreaValue()', 50);
Related
I have a textarea with keyDown listener
<textarea onKeyDown={handleKeyDown}></textarea>
And the handleKeyDown function:
const handleKeyDown = event => event.preventDefault()
When I typing from windows PC, Chrome Version 90.0.4430.212 (Official Build) (64-bit) everything works perfect: text is not appearing in textarea.
When I typing from mac, chrome 90 everything works perfect too.
But when I grab my Android Phone (Samsung, Android 11), open Mobile Chrome (90.0.4430.210) and start typing, text IS appearing in textarea. And the worst thing is that the only mention about this bug I found is this question, which will be referenced by bots when marking my question as duplicate. There is no answer: maxlength attribute not working anymore.
This is the list of things I tried to prevent mobile chrome from input:
Maxlength = 0 (no effect at all)
readOnly (=disabled, I need focus and selection)
blocking onKeyUp, onKeyPress, onPaste, onCut, onInput
I also tried to manually set value of textarea to '' but if I set a listener which does this, my textarea become completely broken: each time I press key on keyboard, it duplicates all previous content.
My question is how to prevent input, maybe there is a hack or trick? Please don't send me to android report forums where this bug may be discussed or another questions...
I would first check to see if the events are working when adding eventlisteners. There must be a way to handle key events on Android:
<textarea id="mytextarea"></textarea>
let mytextarea = document.getElementById('mytextarea');
mytextarea.addEventListener('keydown', (event) => {
console.log(event.code);
});
If this is not working; you asked for a hack or trick: try the focus and blur events, then you could run a function on interval that clears the textarea. Note that this blocking trick is something that goes against good user experience conventions, so should be considered a hack/trick for sure ;-)
<textarea id="mytextarea"></textarea>
let int1;
let mytextarea = document.getElementById('mytextarea');
mytextarea.addEventListener('focus', () => {
clearInterval(int1);
int1 = setInterval(() => {
mytextarea.value = '';
}, 10)
});
mytextarea.addEventListener('blur', () => {
clearInterval(int1);
});
Instead of using event.key or event.code properties of keypress (keyup, keydown) event, I have used event.inputType, event.data of update event and added few restricting attributes to the input tag to overcome 'smartness' of mobile keyboard app. It is an ugly hack but worked for my purpose.
HTML:
<textarea id="inpt" autocapitalize="none" autocomplete="off" autocorrect="off" spellcheck="false" ></textarea>
</div>
JS:
var inpt = document.getElementById('inpt');
inpt.addEventListener('input', do_on_input);
function do_on_input(e) {
if( e.inputType == "insertText"){
console.log( e.data );
}
if( e.inputType == "deleteContentBackward"){
console.log('Backspace');
}
if( e.inputType == "insertCompositionText"){
// without restrictive attribbutes you will need to deal with this type events
}
}
The following snippet works great in chrome/edge/safari. In Firefox the textarea gets focused, but the pressed character isn't being added to the textarea - the first character will be always missing.
document.addEventListener('keypress', (event) => {
document.querySelector('#input').focus();
});
<textarea id="input"></textarea>
How can I make this behave consistently across all browsers?
Here's how you can make it work without browser sniffing: When the keypress happens, bind a handler to the input event on the textarea, and also set a 0-ms timeout.
If the browser accepted the pressed key for the textarea, the input handler will run before the timeout (because the input event fires synchronously). If that happens, you know the browser has handled the keypress correctly, and you can cancel the timeout.
Then, if the timeout fires, you know that the input event hasn't fired and thus the character hasn't been added to the textarea, and you do it programmatically.
In both the event handler and the timeout handler, you unbind the event handler – it should run at most once (per key press).
var textarea = document.getElementById("input");
document.addEventListener("keypress", function (event) {
if (event.target === textarea) {
return;
}
var eventHandler = function () {
textarea.removeEventListener("input", eventHandler);
clearTimeout(timeoutId);
console.log("input event");
}
var timeoutHandler = function () {
var character = ("char" in event) ? event.char : String.fromCharCode(event.charCode);
textarea.value += character;
textarea.removeEventListener("input", eventHandler);
console.log("timeout fired");
}
timeoutId = setTimeout(timeoutHandler, 1);
textarea.addEventListener("input", eventHandler);
textarea.focus();
});
<textarea id="input"></textarea>
<p style="background: #ccc">
<b>Click here</b> to make sure the document is focused, but the textarea is not. Then press a key.
</p>
If you try the above snippet in Firefox, the console will say "timeout fired". In all other browsers it will say "input event". In either case, your pressed key is added to the textarea.
Some notes:
Technically, for consistent behavior you'd need to do more than just append the character to the end; you'd also have to look at things like cursor position and text selection. This may be overkill however.
If you need to support really old browsers, you might want to do a feature check for availability of the input event.
If you have other code that relies on the textarea changing synchronously upon keypress, you'll probably have to make updates there.
While this code will not work if any other browsers share the same behavior as Firefox, the following code will add any key input, given that it is a character whose string length is 1, when the code is run on Firefox:
var mozFocused = false;
document.addEventListener('keypress', (event) => {
document.querySelector('#input').focus();
var isFirefox = typeof InstallTrigger !== 'undefined';
if (isFirefox && !mozFocused && event.key.length === 1) {
mozFocused = true;
document.querySelector('#input').value += event.key;
}
});
document.querySelector('#input').addEventListener('blur', (event) => {
mozFocused = false;
});
<textarea id="input"></textarea>
Again, note that this does not guarantee it to work across all browsers, as this was a fix for Firefox specifically, but, if you see the same behavior occurring in other browsers, I used the answer from this SO post to detect the current browser the client is using (assuming it is in the list of the browsers that this post discusses): How to detect Safari, Chrome, IE, Firefox and Opera browser?
I want to limit the number of characters in a textarea to 255 but still allow keys like Backspace, Delete, arrow keys, Home, End, PgUp, etc. I also want to allow Ctrl+C, Ctrl+X, Ctrl+V, Ctrl+A shortcuts inside a textarea. The number of characters should never be more than 255 even when text was pasted by Ctrl+C or right click > paste. With browsers other than Opera, I can simply use the maxlength property, but it is unfortunately not supported in Opera.
I looked at some of the solutions of SO, but none of them seems to complelety implement what I want. And those solutions are somewhat ugly because some of them hard code many keyCodes. (Maybe that is the only approach.)
How should I approach this to solve this problem?
Concerning Opera, you can just handle the input event:
$('textarea').on('input', function() {
if (this.value.length > 255) this.value = this.value.substring(0, 255);
});
Fiddle
Note: No IE<=8 support. This can technically be shimmed with MS's propertychange event, but that's rather buggy when pasting on IE8 textarea, figures.
Just made this slightly crude snippet to automatically shim textareas' maxLength:
$(function() {
var helper = document.createElement('textarea');
//if (!('maxLength' in helper)) {
var supportsInput = 'oninput' in helper,
ev = supportsInput ? 'input' : 'propertychange paste keyup',
handler = function() {
var maxlength = +$(this).attr('maxlength');
if (this.value.length > maxlength) {
this.value = this.value.substring(0, maxlength);
}
};
$('textarea[maxlength]').on(ev, supportsInput ? handler : function() {
var that = this;
setTimeout(function() {
handler.call(that);
}, 0);
});
//}
});
Demo
Tested in Opera 12.10 and IE8.
This will automatically patch textareas that have a maxlength attribute on DOM ready. You may obviously delegate the event (in case there are dynamically generated textareas) by attaching it to an ancestor and using textarea[maxlength] as a descendant selector, e.g.:
$(document).on(ev, 'textarea[maxlength]', ...)
And finally, the feature detection is commented out because Opera does have a maxLength property on textareas, it parses the value the html to the DOM maxLength property as well as having the default value (-1) for textareas without maxLength, it is just that it apparently has no effect in Opera.
I can't find a way to properly feature detect this Opera bug so the feature detection part is commented out, that is, the code above attaches listeners to textareas independently of whether the browser supports maxLength. Feel free to edit the answer in case you manage to feature detect Opera's bug. Though this seems to be a very localized bug that shall be fixed soon enough.
I don’t want user to allow pasting of any non Alphanumeric characters on a text box.
How do I restrict this in Javascript?
Thanks!!
Using jQuery, this is one way to do it:
HTML:
<form name='theform' id='theform' action=''>
<textarea id='nonumbers' cols='60' rows='10'> </textarea>
</form>
JavaScript:
$().ready(function(){
$("textarea#nonumbers").keyup(removeextra).blur(removeextra);
});
function removeextra() {
var initVal = $(this).val();
outputVal = initVal.replace(/[^0-9a-zA-Z]/g,"");
if (initVal != outputVal) {
$(this).val(outputVal);
}
};
Try it out here.
EDIT: As remarked in the comments, the original (using the .keyup() event) would have left open the possibility of pasting via the mouse context menu, so I've added a .blur() event. .change() would have been possible too, but there are reports of bugginess. Another option is using .focusout(). Time to experiment...
You can use the onblur event of text box.
function remove()
{
var otxt=document.getElementById('txt1');
var val=otxt.value;
for(i=0;i<val.length;i++)
{
var code=val.charCodeAt(i);
if(!(code>=65 && code<=91) && !(code >=97 && code<=121) && !(code>=48 && code<=57))
{ otxt.value=""; return ; }
}
}
<input type="text" id="txt1" onblur="remove();" />
It will remove all value of text box when you input non alphanumeric value.
Why has no one suggested using the OnPaste event? That is fully supported in IE, Safari and Chrome.
Docs for using OnPaste in IE
Docs for using OnPaste in Webkit
In JQuery, that would look like this:
$(input).bind("paste", function(e){ RemoveNonAlphaNumeric(); })
That covers 75% of the browser market.
If you use JQuery, OnPaste is automatically normalized in Firefox so that it works there too. If you can't use JQuery, there is an OnInput event that works.
The working solution is to use a fast setTimeout value to allow the input's value property to be filled.
Basically like this:
$("input").bind("paste", function(e){RemoveAlphaChars(this, e);});
function RemoveAlphaChars(txt, e)
{
setTimeout(function()
{
var initVal = $(txt).val();
outputVal = initVal.replace(/[^0-9]/g,"");
if (initVal != outputVal)
$(txt).val(outputVal);
},1);
}
I have tested this in IE, Chrome and Firefox and it works well. The timeout is so fast, you can't even see the characters that are being removed.
I'm not sure how you can prevent pasting, but you can filter the contents either on the submit or on the change event.
It's better to validate form - check this great jQuery's plugin - http://docs.jquery.com/Plugins/Validation/ and you can use the "number" rule : http://docs.jquery.com/Plugins/Validation/Methods/number. Really simple and easy to tune thing!
Assuming:
<textarea id="t1"/>
You could modify the onchange event handler for the textarea to strip out anything not alphanumeric:
document.getElementById('t1').onchange = function () {
this.value = this.value.replace(/\W/,'');
}
There are many ways the value of a <input type="text"> can change, including:
keypresses
copy/paste
modified with JavaScript
auto-completed by browser or a toolbar
I want my JavaScript function to be called (with the current input value) any time it changes. And I want it to be called right away, not just when the input loses focus.
I'm looking for the cleanest and most robust way to do this across all browsers (using jQuery preferably).
This jQuery code uses .bind() to catch immediate changes to any element, and should work across all browsers:
$('.myElements').each(function() {
var elem = $(this);
// Save current value of element
elem.data('oldVal', elem.val());
// Look for changes in the value
elem.bind("propertychange change click keyup input paste", function(event){
// If value has changed...
if (elem.data('oldVal') != elem.val()) {
// Updated stored value
elem.data('oldVal', elem.val());
// Do action
....
}
});
});
However, note that .bind() was deprecated in jQuery version 3.0. Anyone using jQuery version 1.7 or newer should use .on() instead.
A real-time solution for jQuery >= 1.7 is on
$("#input-id").on("change keyup paste", function(){
dosomething();
})
if you also want to detect "click" event, just:
$("#input-id").on("change keyup paste click", function(){
dosomething();
})
if you're using jQuery <= 1.6, just use bind or live instead of on.
Unfortunately, I think setInterval wins the prize:
<input type=text id=input_id />
<script>
setInterval(function() { ObserveInputValue($('#input_id').val()); }, 100);
</script>
It's the cleanest solution, at only 1 line of code. It's also the most robust, since you don't have to worry about all the different events/ways an input can get a value.
The downsides of using 'setInterval' don't seem to apply in this case:
The 100ms latency? For many applications, 100ms is fast enough.
Added load on the browser? In general, adding lots of heavy-weight setIntervals on your page is bad. But in this particular case, the added page load is undetectable.
It doesn't scale to many inputs? Most pages don't have more than a handful of inputs, which you can sniff all in the same setInterval.
Binding to the input event seems to work fine in most sane browsers. IE9 supports it too, but the implementation is buggy (the event is not fired when deleting characters).
With jQuery version 1.7+ the on method is useful to bind to the event like this:
$(".inputElement").on("input", null, null, callbackFunction);
Unfortunately there is no event or set of events that matches your criteria. Keypresses and copy/paste can both be handled with the keyup event. Changes through JS are trickier. If you have control over the code that sets the textbox, your best bet is to modify it to either call your function directly or trigger a user event on the textbox:
// Compare the textbox's current and last value. Report a change to the console.
function watchTextbox() {
var txtInput = $('#txtInput');
var lastValue = txtInput.data('lastValue');
var currentValue = txtInput.val();
if (lastValue != currentValue) {
console.log('Value changed from ' + lastValue + ' to ' + currentValue);
txtInput.data('lastValue', currentValue);
}
}
// Record the initial value of the textbox.
$('#txtInput').data('lastValue', $('#txtInput').val());
// Bind to the keypress and user-defined set event.
$('#txtInput').bind('keypress set', null, watchTextbox);
// Example of JS code triggering the user event
$('#btnSetText').click(function (ev) {
$('#txtInput').val('abc def').trigger('set');
});
If you don't have control over that code, you could use setInterval() to 'watch' the textbox for changes:
// Check the textbox every 100 milliseconds. This seems to be pretty responsive.
setInterval(watchTextbox, 100);
This sort of active monitoring won't catch updates 'immediately', but it seems to be fast enough that there is no perceptible lag. As DrLouie pointed out in comments, this solution probably doesn't scale well if you need to watch lots of inputs. You can always adjust the 2nd parameter to setInterval() to check more or less frequently.
Here is a solution that doesn't make use of jQuery (Its really quite obsolete and not necessary these days)
Using the event "input" you can look for any kind of change:
Deleting, Backspacing, Pasting, Typing, anything that will change the inputs value.
The input event is directly related to the text input. ANY time the text is changed in ANY fashion, input is dispatched.
document.querySelector("#testInput").addEventListener("input", test);
function test(e) {
var a = document.getElementById('output');
a.innerText += "Detected an Update!\n";
}
<input id="testInput">
<br>
<a id="output"></a>
Here is a slightly different solution if you didn't fancy any of the other answers:
var field_selectors = ["#a", "#b"];
setInterval(function() {
$.each(field_selectors, function() {
var input = $(this);
var old = input.attr("data-old-value");
var current = input.val();
if (old !== current) {
if (typeof old != 'undefined') {
... your code ...
}
input.attr("data-old-value", current);
}
}
}, 500);
Consider that you cannot rely on click and keyup to capture context menu paste.
Add this code somewhere, this will do the trick.
var originalVal = $.fn.val;
$.fn.val = function(){
var result =originalVal.apply(this,arguments);
if(arguments.length>0)
$(this).change(); // OR with custom event $(this).trigger('value-changed');
return result;
};
Found this solution at val() doesn't trigger change() in jQuery
I have created a sample. May it will work for you.
var typingTimer;
var doneTypingInterval = 10;
var finaldoneTypingInterval = 500;
var oldData = $("p.content").html();
$('#tyingBox').keydown(function () {
clearTimeout(typingTimer);
if ($('#tyingBox').val) {
typingTimer = setTimeout(function () {
$("p.content").html('Typing...');
}, doneTypingInterval);
}
});
$('#tyingBox').keyup(function () {
clearTimeout(typingTimer);
typingTimer = setTimeout(function () {
$("p.content").html(oldData);
}, finaldoneTypingInterval);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<textarea id="tyingBox" tabindex="1" placeholder="Enter Message"></textarea>
<p class="content">Text will be replace here and after Stop typing it will get back</p>
http://jsfiddle.net/utbh575s/
We actually don't need to setup loops for detecting javaScript changes.
We already setting up many event listeners to the element we want to detect. just triggering any un harmful event will make the job.
$("input[name='test-element']").on("propertychange change click keyup input paste blur", function(){
console.log("yeh thats worked!");
});
$("input[name='test-element']").val("test").trigger("blur");
and ofc this is only available if you have the full control on javascript changes on your project.
Although this question was posted 10 years ago, I believe that it still needs some improvements. So here is my solution.
$(document).on('propertychange change click keyup input paste', 'selector', function (e) {
// Do something here
});
The only problem with this solution is, it won't trigger if the value changes from javascript like $('selector').val('some value'). You can fire any event to your selector when you change the value from javascript.
$(selector).val('some value');
// fire event
$(selector).trigger('change');
Or in a single line
$(selector).val('some value').trigger('change');
Well, best way is to cover those three bases you listed by yourself. A simple :onblur, :onkeyup, etc won't work for what you want, so just combine them.
KeyUp should cover the first two, and if Javascript is modifying the input box, well I sure hope it's your own javascript, so just add a callback in the function that modifies it.
Here's a working example that I'm using to implement an autocomplete variation the populates a jqueryui selector (list), but I don't want it to function exactly like the jqueryui autocomplete which does a drop-down menu.
$("#tagFilter").on("change keyup paste", function() {
var filterText = $("#tagFilter").val();
$("#tags").empty();
$.getJSON("http://localhost/cgi-bin/tags.php?term=" + filterText,
function(data) {
var i;
for (i = 0; i < data.length; i++) {
var tag = data[i].value;
$("#tags").append("<li class=\"tag\">" + tag + "</li>");
}
});
});
Can't you just use <span contenteditable="true" spellcheck="false"> element in place of <input type="text">?
<span> (with contenteditable="true" spellcheck="false" as attributes) distincts by <input> mainly because:
It's not styled like an <input>.
It doesn't have a value property, but the text is rendered as innerText and makes part of its inner body.
It's multiline whereas <input> isn't although you set the attribute multiline="true".
To accomplish the appearance you can, of course, style it in CSS, whereas writing the value as innerText you can get for it an event:
Here's a fiddle.
Unfortunately there's something that doesn't actually work in IE and Edge, which I'm unable to find.
you can simply identify all changers in the form, like this
//when form change, show aleart
$("#FormId").change(function () {
aleart('Done some change on form');
});
You can bind the 'input' event to <input type="text">. This will trigger every time the input changes such as copy, paste, keypress, and so on.
$("#input-id").on("input", function(){
// Your action
})