preventDefault not blocking keys that use Alt modifier - javascript

I am making a chrome extension that occasionally prevents all keyboard inputs from reaching the webpage temporarily (as the extension's handler performs its own tasks with the keyboard inputs that it catches). And the extension does a good job listening to all of those inputs.
HOWEVER, some keyboard inputs still get through the webpage! Specifically, certain keyboard inputs that use the Alt modifier key reach the webpage. I have replicated the issue with this code:
$(window).get(0).addEventListener("keypress",
function(e){
e.stopPropagation();
e.preventDefault();
}, true);
You can interact with the code in this jsfiddle: https://jsfiddle.net/Sophtware/5ucefew2/
Can someone help me figure out why this is happening and how to fix it?
EDIT #1: I found out that the symbols that are coming through the preventDefault call are things like accents or other "combining characters" (like ´, ˆ, ¨). In fact, once a combining character has been typed, the next character typed will always also fail to be blocked.

Well, first, you didn't have jQuery loaded in your Fiddle.
Second, you need to use the keydown event, not keypress because keypress doesn't fire when keys that don't produce visible characters are pressed. This is why you found that some characters work with it and others don't.
Third, your event binding code isn't really written correctly. There is no need to use get(0) when jQuery returns a wrapped set that contains only one item. And since there is only one window or document, that code isn't needed.
Additionally, you can check explicitly for the ALT key being pressed.
Lastly, please don't post your code to 3rd party sites because those links can become broken over time. Instead post your code snippets right here.
See comments inline:
// Set the event on the document, but then test for the input element when it happens
$(document).on("keydown", "input[type='text']", function(e){
// Check the event for the ALT key press
console.log("Keystroke cancelled!");
e.preventDefault();
e.stopPropagation();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h3>
Type in Alt + 'e', Alt + 'i', or other Alt combinations! They get typed in even though they're not supposed to!
</h3>
<p>
Strangely, Alt+j and Alt+k cannot be written more than once in a row and act strange in general.
If you type in Alt + e and then a regular letter key press, the letter gets typed! ??????
</p>
<input type="text">

Related

Input type=email's change event is not firing when the value entered is whitespace

I've added an on 'change' event listener to a type=email input element. When I add a couple space characters into the email field, then lose focus on that element, the change event doesn't seem to be firing.
However, this exact scenario works just fine with type=text input elements.
What's going on?
$('input[type="email"]').change(e => {
console.log('Triggered!');
});
Browser: Chrome Version 63.0.3239.132 (Official Build) (64-bit)
I originally said that it looks like there is an automatic trim operation performed on email fields because the length of the value is coming back at 0 after typing some spaces and leaving the field, but upon returning to the field, the spaces remain in the element, so they aren't getting trimmed out.
I suspect that, because spaces are not valid for this input type, they are not considered part of the value, thus the value doesn't change when you enter them and the change event doesn't fire.
Type some spaces in the field and then hit TAB to leave the field, but then return to the field. The spaces will still be there.
$('input[type="email"]').on("blur", function(e){
console.log(this.value.length);
});
$('input[type="email"]').on("change", function(e){
console.log("Change fired!");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="email">
You can use something like that:
$('input[type="email"]').on('focusout', {
console.log('Triggered!');
var $trig = $(this);
$trig.attr('trimmed', $trig.val().toString().trim());
$trig.val( '').val($trig.attr('trimmed'));
});
But, as answered above, input[type="email"] does not count whitespaces. It is only works as fast hack;
I am facing the same problem with React, and I don't want to reflect the error on the onBlur event (as other solutions here). I don't think an error should be reflected in any input by the simple fact of removing the mouse from that input. For me that's not User friendly,... AT ALL.
Why?
Because the User might have decided to remove the mouse from that
Input only because he/she simply wants to copy something from somewhere else first,... and then past it there (and/or to past it somewhere else). So technically there is no mistake there yet.
Because I simply want to fill another input field of the form first.
Why? Becase that's precisely the field's value I already copied from
somewhere else, so that's the value I have stored in clipboard, and
it doesn't goes where my mouse landed by default. Or simply because
I just want to! I'm the User, so I can choose the order to
fill the form!
For me is more than enough with validating what the User has written and/or removed/deleted from the Inputs (onChange validation) AND also what the User finally decides to send (onSubmit validation). A proper combination of onChange and onSubmit validation is the perfect healthy balance between thoroughness and User friendly.
A Solomonic "solution":
I am using a custom validation hook. As I can not change the behavior of the input with a type email regarding the white spaces in an OnChange event,... then I decided to use a workaround, which is simply avoiding the typing of white spaces and that's it, as the onChange event won't trigger anyway.
const preventWhiteSpaceOnKeyDown = (e) => {
if (e.key === " ") {
e.preventDefault();
}
}
.
.
.
<input
type={"email"}
id='commentEmail'
name='commentEmail'
required={true}
autoFocus={true}
ref={emailInputRef}
value={emailState}
onChange={emailInputChangeHandler}
onKeyDown={preventWhiteSpaceOnKeyDown}
/>
This is not a "solution". There is no clean solution for this. But after this at least my input[type=email] element won't hold useless white spaces.
input[type="email"] does not fire change event, use blur event instead:
$('input[type="email"]').blur(function(){
console.log('blur event is fired.');
});

Fill an input field that is not of type text and that triggers events in CasperJS

I have to do automated tests on a website and I want to use CasperJS to learn. For proprietary reasons I can not give too much code.
Here is the example of the input that I am trying to fill:
<input data-bind="value: firstname, valueUpdate: ['blur'], css: {valid:(firstname.isValid() )} " title="" class="valid" aria-required="true" id="firstname" name="firstname">
As you can see, this input is not of type text and has no value attribute. Therefore, I can not use the casper.fill() method. Furthermore, if I enter the web page scope using evaluate() and change the input value using document.querySelector, the change will not be permanent as of the events attached to the text change on the input will not be triggered.
Here is my code:
this.waitForSelector('#memberTitle', function then(){
var testname = 'thisIsNotPermanent';
this.evaluate(function(testname){
document.querySelector('#firstname').value = testname;
}, testname);
});
If I capture the screen right after, I will see my text written in the input box. However, if I wait 500ms and take another capture, the text is gone as, I suppose, the events are triggered or just cleaned because it actually failed to trigger correctly.
The events attached to the input are of Blur, Change and Keypress.
Using CasperJS, how could I go to the lowest level possible to mimic a user using his keyboard and fully use the website's functionalities already in place?
The whole point of those tests are to work with what is in place. The idea is to not have to manually go through the JavaScript of the web site.
That's exactly what the casper.sendKeys(selector, keys) function is for which will send native keypresses and (hopefully) trigger the events on that text element:
this.waitForSelector('#memberTitle', function then(){
var testname = 'thisIsNotPermanent';
this.sendKeys('#firstname', testname);
}).wait(20, function(){
this.capture('screenshot.png');
});
<input> elements without a type attribute default to Text type.
This answer is here to complete the question from another angle. As Artjom B. mentionned, the correct way to fill an input and to trigger its events is by using the sendKeys() function. However, if you ever have a case, like mine, where the events will not trigger or will take a certain amount of time, know that you can trigger those manually.
If you use the firefox inspector tool, you will see that your input or tag will have an event attached to it marked as ev. If you select it, you will have a breakdown of all the events, in order, that are triggered.
You can see that the jQuery click() event will be called. In casperjs, from the evaluate scope you can now do this :
this.evaluate(function(){
$(".discard-answer").click();
})
From there, you can chain jQuery events, like in my case where I had to .blur().change().click();
It is important to know if the event is jQuery or not.
Hope this helps.

is there a way to get keyboard input on a webpage that listens for it?

I'm working on a Chrome extension whose content script injects a bunch of elements in a webpage, including an input element of type text, on specified actions.
the problem is that while on a webpage like Facebook's home page, which listens for keyboard input (e.g., P), the extension's input element loses focus, which goes to Facebook's "what's on your mind?" section in case of the P.
I tried getting focus back to the input element programtically, and while that seems to be partially working, as it takes focus back from the "what's on your mind?" section immediately, it still doesn't write the 'P' into the text field.
is there anyway to workaround that?
update #0: the code that I tried for regaining focus was as simple as that:
searchBar.onblur = searchBar.focus;
update #1: my input element is inside a shadow DOM. apparently the element doesn't lose focus when it's not part of a shadow DOM. any idea on how to get that to work with the shadow DOM?
Check out this example. You can listen for keyboard events on the highest level (which is document), unless the site blocks propagation of the event.
document.addEventListener('keypress', function(e) {
console.log(e);
}, false);
document.getElementById('text3').addEventListener('keypress', function(e) {
console.log(e);
}, false);
<textarea id="text1"></textarea>
<textarea id="text2"></textarea>
<textarea id="text3"></textarea>
<textarea id="text4"></textarea>
I don't use Facebook; are you saying that when someone types a P, that causes the focus to move to "What's on your mind?" Because if the sequence of events is keypress --> Facebook takes focus --> you take focus back, the keypress didn't occur while your input field had focus, so the typed letter wouldn't show up.
You might have to put those letters into your input's value yourself by listening to keypresses, checking if they missed the input field, converting the keycode into the appropriate letter, and appending it to the input's value.

Autofocus input somehow catching a keypress?

I'm implementing a couple of shortcut keys for my MVC web app and I'm running into an issue. The app allows users to answer multiple choice or short answer questions, and I'd like to make multiple choice questions answerable by just pressing 1,2,3,4 etc. Here's what I have right now:
$(document).on("keypress", function (e) {
self.someKeyPressed(e);
});
[...]
someKeyPressed: function(e) {
// locate all the multiple choice buttons in this view
var buttons = this.$el.find('button');
if(_.contains(_.range(49,55), e.which) && buttons.length) {
index = e.which - 49;
var answer = $(buttons[index]).text();
this.answerQuestion(answer);
}
e.stopPropagation();
},
This works fine, except for the case when a multiple-choice question is immediately followed up by a short answer question. In this case, the same <div> used to contain the elements, is instead replaced with:
<input placeholder="answer goes here" autofocus="autofocus">
And here's where the problem is: whatever shortcut I used on my keyboard to answer the multiple choice question shows up in the as if the user had typed it in, even though that view didn't exist at the time of the keypress. I tried to wire up an event on "input keypress" and that doesn't even go off in this situation. Interestingly enough, removing autofocus fixes the issue, but unfortunately I need it for for the user experience to be pleasant.
What's going on here? Is there anything I can do to prevent the keypress from making it into the input until I'm ready?
Edit: it looks like adding e.preventDefault() right after e.stopPropagation() did the trick. Without it, the shortcut number would be typed into the input as soon as the jquery event dispatcher logic completed, but no sooner.
It looks like calling e.preventDefault() does the trick, even though I'd still love to find out one day why exactly this makes a difference.

How can I get the new value of a HTML text element in a key event handler, also for special keys?

Lets suppose we have a html text input element and it has text "abc" and cursor is between "b" and "c". If we press backspace key then how can we get the value "ac"?
Please note that in case of special keys KeyPress event do not fire. The only events that fire are KeyDown and KeyUp and none of them has the value after the effect of special key is applied. The effect is visible after the eventhandlers of these events exit but since we have only these two events we have to somehow get the affected/latest value inside these events.
We can go to a complex way by manually applying the effect ourselves but its very very complicated given the facts that we have to find the cursor position, write different code for different special keys and bring browser compatibility. The browser, whichever it is, is already applying the effect once the eventhandlers exit but is there some way to get that latest value in those events without manually applying it or in some other event?
Please note that I am not searching for "how to find which key is pressed". I can find that by looking at the event object inside the KeyDown or KeyUp event handlers. I want to apply the effect of the special key without using a lot of manual code.
I have already looked at Capturing HTML Text Input Key press after key has been applied?. Its talking about a different thing than my question.
My ultimate task is to have a web page with only two controls: a textbox and a button. The button is initially disabled. User can type in textbox and on every key its checked that there is some text in the textbox, if there is then button is enabled, if not then button is disabled. The difficult part is to take into consideration special keys such as delete, enter, tab, backspace.
Note: I do not want to work on the blur eventhandler of the HTML text element because it affects the tab order.
Example using jQuery. The target value is stored on the title attribute, but you could make this an ajax request, or whatever logic you need. In the following case, typing 'abc' in the text box will make the go button enabled.
HTML:
<input type="text" title="abc" id="in">
<input type="button" id="go" value="Go" disabled="disabled">
Javascript:
$("#in").keyup(function() {
if($(this).val() == $(this).attr("title")) {
$("#go").removeAttr("disabled");
}
});
JSFiddle

Categories