I would like to populate a textarea by triggering keyboard events, such as keydown (I'm doing this for a test case).
I have added a snippet (below) to show the code I'm using to create and trigger the event. The event fires, but the textarea never receives the value of keyCode which is the letter A.
What do I need to do to see the letter in the textarea? I'm currently running the snippet in the Chrome console but it should also work in IE9.
var t = document.getElementById('foo');
// Event creation
var event = document.createEvent('HTMLEvents');
event.initEvent('keydown', true, true);
event.keyCode = 65;
event.which = 65;
// Listener for demo purpose
t.addEventListener('keydown', function (e) {
document.getElementById('fired').value = e.type + ' fired';
});
// Event trigger
t.dispatchEvent(event);
<textarea id="foo"></textarea>
<br>
<input id="fired" value="">
The keydown event is fired when a key is pressed down but it's not the responsible for write the data in the DOM elements.
The thing is; If the user writes on the <textarea> first the character is added to elements value and then the keyDownevent is triggered. However in your case you're directly triggering the event so the first step which is adding the character to the value for <textarea> is not happening.
You have two options, do it in the browser way write the value and then dispatch the event
t.value = t.value + String.fromCharCode(e.keyCode);
t.addEventListener('keydown', function (e) {
document.getElementById('fired').value = e.type + ' fired';
});
Or also you can write the value of the <textarea> on the keyDown event:
// Listener for demo purpose
t.addEventListener('keydown', function (e) {
t.value = t.value + String.fromCharCode(e.keyCode);
document.getElementById('fired').value = e.type + ' fired';
});
however if you want to use this second approach for user interaction it's a nonsense because in the case that the users inputs the data, the data will be write it twice (one for the user input and the another one in the event).
Hope this helps,
Javascript sending key codes to a <textarea> element
I had a look around and this seems more relevant than my non-relevant answer before. Sorry about that. I know this is jquery, but the premise is the same.
adding this in the event would work
document.getElementById('foo').innerHTML += String.fromCharCode(e.keyCode);
here it is in pure javascript jsfiddle
Why does not the value change after triggering keydown?
In short: you can't change the value of input/texarea with dispatching KeyboardEvent programmatically.
How actually do chars come into input? On MDN you can find the description of Keyboardevent sequence (assuming that preventDefault is not called):
A keydown event is first fired. If the key is held down further and the key produces a character key, then the event continues to be emitted in a platform implementation dependent interval and the KeyboardEvent.repeat read only property is set to true.
If the key produces a character key that would result in a character being inserted into possibly an <input>, <textarea> or an element with HTMLElement.contentEditable set to true, the beforeinput and input event types are fired in that order. Note that some other implementations may fire keypress event if supported. The events will be fired repeatedly while the key is held down.
A keyup event is fired once the key is released. This completes the process.
So, keydown leads to input event by default. But that is true only for trusted events:
Most untrusted events will not trigger default actions, with the exception of the click event... All other untrusted events behave as if the preventDefault() method had been called on that event.
Basically trusted events are those initiated by a user and untrusted events are initiated with a script. In most browsers, each event has an attribute isTrusted indicating if the event is trusted or not.
And how to test KeyboardEvents on inputs then?
Well, first of all, think if you really need a KeyboardEvent handler. Maybe you can do everything in InputEvent handler. That means that you can just set the value of the input in your tests and then trigger InputEvent.
If you still need KeyboardEvent handler than it depends on what is going on in it. E.g. if you call preventDefault in certain conditions then you can check if it was called or not in a test using a spy. Here is an example with sinon as a spy and chai as assertion library.
const myEvent = new KeyboardEvent('keydown', { key: 'a' })
sinon.spy(myEvent, 'preventDefault')
document.getElementById('foo').dispatchEvent(myEvent)
expect(myEvent.preventDefault.calledOnce).to.equal(true)
Related
I'm writing a form validation script and would like to validate a given field when its onblur event fires. I would also like to use event bubbling so i don't have to attach an onblur event to each individual form field. Unfortunately, the onblur event doesn't bubble. Just wondering if anyone knows of an elegant solution that can produce the same effect.
You're going to need to use event capturing (as opposed to bubbling) for standards-compliant browsers and focusout for IE:
if (myForm.addEventListener) {
// Standards browsers can use event Capturing. NOTE: capturing
// is triggered by virtue of setting the last parameter to true
myForm.addEventListener('blur', validationFunction, true);
}
else {
// IE can use its proprietary focusout event, which
// bubbles in the way you wish blur to:
myForm.onfocusout = validationFunction;
}
// And of course detect the element that blurred in your handler:
function validationFunction(e) {
var target = e ? e.target : window.event.srcElement;
// ...
}
See http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html for the juicy details
use 'Focusout' event as it has Bubble up effect..thanks.
ppk has a technique for this, including the necessary workarounds for IE: http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
aa, you can simply add the onblur event on the form, and will call the validation every time you change focus on any of the elements inside it
Wondering if someone out there can shed some light on how to properly use element.triggerHandler() for the paste and keypress events inside of an angularjs unit test.
I have two directives, one for limiting the ability of a user to continue firing keypress events in an element once a length limit has been reached. The second is to prevent the user from pasting text into an element if the length of the text would exceed a limit.
See the following plunker for a full example including my failing tests: https://plnkr.co/edit/5Yyv2cnn3dRKzsj2Lj61?p=preview
For the paste test I know I'm not using the correct syntax but have been unable to find how to properly do this. Any suggestions?
element.triggerHandler('paste', 'astring')
For the keypress test, I believe I'm firing the event correctly but it doesn't seem to be updating the value of the element (retrieved using element.val())
Been stuck on this for a bit, any help would be greatly appreciated. Thanks!
Let's us start with a short breakdown of what might happen (really up to the browser implementation) when a user presses and releases the 1 key with the focus on an input:
Event keydown is fired
Event keypress is fired
Value of input is changed to 1 and event input is fired
Event keyup is fired
There is no way in JS to actually simulate a user pressing a key. What you can simulate are the things that (usually) happen when a user does so, for example the steps above.
The triggerHandler function executes all handlers bound with jQuery for the specified event type on the specific element.
So using triggerHandler with keypress will not simulate a user pressing a key, it will only fire the event, like step 2 above. The value will not be changed, since that happens in another step.
Now, one would think that in your tests for the limitKeypressLength directive, you can simply simulate the first part of step 3 above yourself (just setting the value manually):
for (var i = 0; i < 10; i++) {
element.triggerHandler({type: 'keypress', keyCode: 49});
element.val(element.val() + '1');
}
expect(element.val()).toBe('1111111111');
element.triggerHandler('keypress', {which: 49});
element.val(element.val() + '1');
expect(element.val()).toBe('1111111111');
This will not work however, since even if the eleventh keypress event is caught in your directive, the code below will still execute and update the value.
The basic functionality of the limitKeypressLength directive is to listen on the keypress event and either call event.preventDefault or not based. This is what you want to test.
For example:
// Set value to something longer than allowed
element.val('123456789123456789');
// Create the event
var e = jQuery.Event('keypress', {
keyCode: 49
});
// Create a spy
spyOn(e, 'preventDefault');
// preventDefault should not have been called yet
expect(e.preventDefault).not.toHaveBeenCalled();
// Trigger the event
element.triggerHandler(e);
// Assert that preventDefault has been called
expect(e.preventDefault).toHaveBeenCalled();
Demo: https://plnkr.co/edit/ktmcBGSuTdMnvqVRlkeQ?p=preview
Now you can as easily test for when the elements value is set to equal/below the allowed value.
Basically the same goes for the limitPasteLength directive, since its purpose is also to call preventDefault based on a condition, only that there is some additional mocking to do.
When using jquery .change on an input the event will only be fired when the input loses focus
In my case, I need to make a call to the service (check if value is valid) as soon as the input value is changed. How could I accomplish this?
UPDATED for clarification and example
examples: http://jsfiddle.net/pxfunc/5kpeJ/
Method 1. input event
In modern browsers use the input event. This event will fire when the user is typing into a text field, pasting, undoing, basically anytime the value changed from one value to another.
In jQuery do that like this
$('#someInput').bind('input', function() {
$(this).val() // get the current value of the input field.
});
starting with jQuery 1.7, replace bind with on:
$('#someInput').on('input', function() {
$(this).val() // get the current value of the input field.
});
Method 2. keyup event
For older browsers use the keyup event (this will fire once a key on the keyboard has been released, this event can give a sort of false positive because when "w" is released the input value is changed and the keyup event fires, but also when the "shift" key is released the keyup event fires but no change has been made to the input.). Also this method doesn't fire if the user right-clicks and pastes from the context menu:
$('#someInput').keyup(function() {
$(this).val() // get the current value of the input field.
});
Method 3. Timer (setInterval or setTimeout)
To get around the limitations of keyup you can set a timer to periodically check the value of the input to determine a change in value. You can use setInterval or setTimeout to do this timer check. See the marked answer on this SO question: jQuery textbox change event or see the fiddle for a working example using focus and blur events to start and stop the timer for a specific input field
If you've got HTML5:
oninput (fires only when a change actually happens, but does so immediately)
Otherwise you need to check for all these events which might indicate a change to the input element's value:
onchange
onkeyup (not keydown or keypress as the input's value won't have the new keystroke in it yet)
onpaste (when supported)
and maybe:
onmouseup (I'm not sure about this one)
With HTML5 and without using jQuery, you can using the input event:
var input = document.querySelector('input');
input.addEventListener('input', function()
{
console.log('input changed to: ', input.value);
});
This will fire each time the input's text changes.
Supported in IE9+ and other browsers.
Try it live in a jsFiddle here.
As others already suggested, the solution in your case is to sniff multiple events.
Plugins doing this job often listen for the following events:
$input.on('change keydown keypress keyup mousedown click mouseup', handler);
If you think it may fit, you can add focus, blur and other events too.
I suggest not to exceed in the events to listen, as it loads in the browser memory further procedures to execute according to the user's behaviour.
Attention: note that changing the value of an input element with JavaScript (e.g. through the jQuery .val() method) won't fire any of the events above.
(Reference: https://api.jquery.com/change/).
// .blur is triggered when element loses focus
$('#target').blur(function() {
alert($(this).val());
});
// To trigger manually use:
$('#target').blur();
If you want the event to be fired whenever something is changed within the element then you could use the keyup event.
There are jQuery events like keyup and keypress which you can use with input HTML Elements.
You could additionally use the blur() event.
This covers every change to an input using jQuery 1.7 and above:
$(".inputElement").on("input", null, null, callbackFunction);
I have a search field that triggers an autocomplete search while typing. I have it trigger on keyup. This works perfectly in most browsers, but in Firefox on Android, this does not work. It seems like the keyup event is not triggered while typing. This only happens if word suggestions is turned on in the Android keyboard settings.
I see on Google search that the autocomplete search works there for the same setup, so it is obviously possible to do. I wonder how? Is it a special event I need to listen to for this to work?
Additionally I have tried to listen to the events change, keydown and keypress, but none is triggered.
HTML:
<input type="text" id="searchField"
autocomplete="off" spellcheck="false" autocorrect="off" />
jQuery event binding:
$('#searchField').keyup(function (e) {
var searchValue = $(this).val();
searchApi._executeAutocomplete(searchValue);
});
Note:
Sometimes, the key event is triggered, which is typically hitting a key that is not resulting in the process of forming a word. The most obvious here is Enter, which always triggers. Another is Space, which triggers because no word contain a space since space is the definition of a word completed. Backspace triggers if the the last character deleted was not within a word. This means it triggers if you just deleted the last remaining letter of a word (so it is the start of the field, or cursor following a space), but not if you deleted some characters at the end of a word where the cursor is still immediately following a letter. Basically, the key event is not triggered if the key press results in some kind of word suggestion from the keyboard app.
As a side note, I can say that everything works fine in Chrome on the same device.
You can use the input event instead, that worked for me in Firefox on Android.
You could bind event handlers to both input and keyup events for backwards compatibility, but in most modern browsers this will fire both:
$('#searchField').bind('input keyup', function(e){
var searchValue = $(this).val();
searchApi._executeAutocomplete(searchValue);
});
Example here:
http://jsfiddle.net/JQ928/3/
I found a solution in this answer to another question. The question was a basically "duplicate the text I write dynamically into another part of the page". The answer was including support for catching changes by non-keyboard actions, like pasting text using mouse. It was solved by starting a sniffer on focus in the text field that checks if the value has changed using setInterval(...). It clears the timer on blur.
This solved my problem which was basically that the key events didn't trigger, as well as the "paste by mouse" issue that I didn't realize was a problem until I found this answer...!
This works, but I'm not sure I am totally happy with this solution, because it uses a sniffer. I would be more happy with using some sort of event that is triggered on value change no matter what the cause of the change is. Using the change event would not work, as that is not triggered until focus leaves the field.
Trough the fact that Firefox on Android doesn't trigger key-events, but also triggers the input-event some kind of weird, (like if you press one key two events get triggerd, and it also triggers the input-event if you leave the input) I had to write my own event:
(function($){
var $event = $.event,
$special = $event.special.fennecInput = {
setup: function(){
$(this).on('input',$special.handler);
},
teardown: function(){
$(this).off('input',$spceial.handler);
},
handler: function(event) {
var context = this,
args = arguments,
dispatch = function() {
event.type='fennecInput';
$event.dispatch.apply(context,args);
};
if($(context).val() != $(context).attr('data-fennecInput-oldval')){
dispatch();
$(context).attr('data-fennecInput-oldval',$(context).val());
}
}
};
})(jQuery);
this event gets only triggered if an input-event happens that changes the value, so it doesn't execute events unnecessary.
I'd like to invoke default keydown event handler from javascript. Is it possible?
If the event has an explicit event handler you can just invoke it directly:
// Precondition - the element has an explicit handler registered
element.onkeydown();
Otherwise, there's no way to explicitly tell the browser to do "what it would have done anyway". The only way to get this to happen is to not stop the event from bubbling - which can be a real pain if you want to set a timeout and then allow the event to continue, it's essentially not possible.
In most cases, though, you can invoke your own code on an event handler and let the keyDown event continue to the browser. And if this isn't possibel for whatever reason, you can usually write your own method that will simulate the effects of the event (e.g. change the content of an input field, submit the form, etc.)
There is no default keydown event. The keydown event occurs when the key is pressed on any form elements, followed immediately by the keypress event, and possibly the textInput event on text box when you enter a value. Then the keyup event is generated when the key is released
The following example shows the use of the onKeyDown event handler to display a message in the text box.
<body><form action="" method="POST" id="myForm"><input type="text" name="myText" onKeyDown="changeVal()"><script type="text/javascript" language="JavaScript">s1 = new String(myForm.myText.value)function changeVal() { s1 = "You pressed a key"
myForm.myText.value = s1.toUpperCase() }</script></form></body>