Simulating a tab keypress using JavaScript - javascript

I'd like to have the browser act as if the user had pressed the Tab key when they click on something. In the click handler I've tried the following approaches:
var event = document.createEvent('KeyboardEvent');
event.initKeyEvent("keypress", true, true, null, false, false, false, false, 9, 0);
this.input.focus()[0].dispatchEvent(event);
And jQuery:
this.input.focus().trigger({ type : 'keypress', which : 9 });
...which I took from here.
The first approach seems to be the best bet, but doesn't quite work. If I change the last two parameters to 98, 98, indeed, a 'b' is typed into the input box. But 9, 0 and 9, 9 (the former of which I took right from the MDC web site) both give me these errors in firebug under FF3:
Permission denied to get property XULElement.popupOpen
[Break on this error] this.input.focus()[0].dispatchEvent(event);
Permission denied to get property XULElement.overrideValue
[Break on this error] this.input.focus()[0].dispatchEvent(event);
Permission denied to get property XULElement.selectedIndex
[Break on this error] this.input.focus()[0].dispatchEvent(event);
Permission denied to set property XULElement.selectedIndex
[Break on this error] this.input.focus()[0].dispatchEvent(event);
I've heard such (with no clear definition of 'such') events are 'untrusted', which might explain these errors.
The second approach causes whatever value I put as event.which to be passed as event.which, but to no effect (even if I use 98 instead of 9, no 'b' is typed in the box.) If I try setting event.data in the object I'm passing, it ends up undefined when the event is triggered. What follows is the code I'm using to view that:
$('#hi').keypress(function(e) {
console.log(e);
});
Any other ideas?

The solution I ended up going with is to create a "focus stealer" div (with tabindex = -1--can have the focus but can't be tabbed to initially) on either side of the area in which I want to manually manage the focus. Then I put a bubbling-true event listener for focus and blur on the whole area. When any focus occurs on the area, the tabindex values are changed to -1, and when any blur occurs, they're changed to 0. This means that while focused in the area, you can tab or shift-tab out of it and correctly end up on other page elements or browser UI elements, but as soon as you focus out of there, the focus stealers become tabbable, and on focus they set up the manual area correctly and shunt the focus over to the element at their end, as if you had clicked on one end or the other of the manual area.

This is the solution I used on our webapp for two custom controls, a pop-up calendar and a pop-up unit / value weight selector (clicking the text box pops up a div with two selects)
function tab_focus(elem)
var fields = elem.form.getElements()
for(var i=0;i<fields.length;i++) {
if(fields[i].id == elem.id){
for(i=i+1;i<fields.length;i++){
if(fields[i].type != 'hidden'){
fields[i].focus()
return
}
}
break;
}
}
elem.form.focusFirstElement();
}
This is using the Prototype framework and expects an extended element(ie $('thing_id')) as its parameter.
It gets the form the element belongs to, and loops through the elements of the form until it finds itself.
It then looks for the first element after it that is not hidden, and passes it the focus.
If there are no elements after it in the form, it moves the focus back the to first element in the form. I could instead find the next form on the page through document.forms, but most of our pages use a single form.

Actually, I guess there is a way, even if it's a major PITA. I can make sure that every element, even if naturally a tab-stop, has an Xtabindex, somehow in the proper order even though I'll be dropping in other people's widgets and so using jQuery to add these after the fact, rather than being able to specify it right in the HTML or other initial building code. Then, my entire form will have a real tabindex. While it has the focus, it will absorb keypresses, and if they're tab or shift+tab, move the fake focus based on Xtabindex. If tab is pressed on the last (or shift+tab on the first) element in the form, it won't gobble the keystroke, thus allowing the browser to properly focus on other page or browser UI elements outside the form using the keyboard.
I can only guess what kinds of unintended side-effects this approach will introduce.
Actually, it's not even a solution, because I still can't fake a tab on the last element using it.

I created a simple jQuery plugin which does solve this problem. It uses the ':tabbable' selector of jQuery UI to find the next 'tabbable' element and selects it.
Example usage:
// Simulate tab key when element is clicked
$('.myElement').bind('click', function(event){
$.tabNext();
return false;
});

I think those errors are from autocomplete. You might be able to turn them off by setting, before you dispatch the event, the autocomplete attribute to 'off'
setAttribute('autocomplete','off')

Related

jquery validation plugin detects errors, but no messages appear, why not?

I'm using the jQuery Validator plugin 1.19.5 on a slightly large (but simple) form generated from a PDF by an online converter to html5
The form has a Submit button implemented as a button with onclick to a javascript function within the formviewer.js file that is part of the conversion to html5. If I open the form in Chrome 107.0.5304.107 Developer Tools, I can see that the Submit button goes to the following code that I added to the success branch of the function that handles the submit in formviewer.js:
success: function() {
const OSHform=$("form").eq(0);
if (OSHform.valid()) {
top.document.location.href = "/Adsentry/completed";
}
else {
alert("Fields did not validate, please fix errors and try again");
}
},
failure: function() {
alert("Form failed to submit, please try again")
}
In a separate script, I invoked validate() on the form element, passing it rules for the fields to validate.
var $j = jQuery;
var OSHform = $j("form");
OSHform.validate({
rules: {
"NAME OF DRIVER": "required",
"EMAIL": "required",
"EMAIL": "email",
"ADDRESS": "required"
}
});
If I omit required fields, or enter an invalid email address in an email field, the call to valid() returns false. And in fact, if I look at the input elements in the Elements tab, I can see that class="error" gets added, and if I correct the error it changes to class="valid". Additionally, with class="error", a label gets added for that element, also with class="error", and correcting the problem adds style="display:none;" to the label.
So everything is great, except that there is no text or message that goes with the label, so its presence/absence, or the presence/absence of display:none on it, has no effect on the normal display of the page.
I have tried stepping through the code in the debugger, but I'm afraid my javascript is so weak I can't really figure out what's going on to the extent of understanding why the messages are not being displayed.
You can play with it live here for the time being, but I can't promise to stop fiddling with it! There are currently only 3 required fields: Name of driver, Address, and Email. If they are all correct, the form submits as intended.
A little disappointed that this didn't even get any comments, let alone answers. On the other hand, it turned out the answer was exactly as anyone even slightly more experienced than me would likely have guessed: errors were being reported in HTML elements, but there was no CSS to put them in the right location on the page. The plugin seemed to be buggy in failing to produce default message text describing the errors; instead, it just produced message text that was simply the name attribute of the erroneous input element. But without appropriate CSS, that name attribute was output in the black strip at the bottom of the page, making it essentially invisible. It took a sharp eye to notice the sudden appearance of "fly specs" at the bottom of the page when clicking the submit button.
The plugin just inserts an HTML element into the DOM immediately following the bad input element. But the files generated from the PDF include a style sheet with selectors using the ID of each input element to give the input element absolute placement on the page. And simply inserting an element into the DOM as the next sibling of the input element, without a style, results in having it rendered at the bottom of the page. Even when I figured out that the lack of CSS was the problem, it took me a while to get something that worked: good old selector specificity in action. All of the input elements were placed using ID selectors with absolute position, and I could find no way to have the simple next-sibling relationship of the message to the input element cause the message to be rendered immediately after the input element. Although it made me feel "icky" to do it, the solution I came up with was to use jQuery to iterate over all the message elements with the "error" class, get the ID of the input element it was reporting, and then use $.css() to get the input element's effective top, left, and width style attributes. Then strip off the trailing "px", multiply by 1 to get a numeric value, add the width to the left numeric value, and specify new top and left attributes using $.css() on the message elements. This put the messages I defined in the messages sub-object of the object passed to the validate constructor appear in the right locations. It only remains a mystery why the default messages didn't appear instead of the names of the input elements for elements that were invalid.

(Why) does accessing outerHeight cancel click/mouseup/focus event?

I've encountered a weird issue - accessing any DOM element's outerHeight, or simply logging that element to browser's console in a blur event seems to cancel the click event that caused the blur - when some special criteria is met.
Here's what I'm trying to do:
There are 2 inputs, first is initially visible, the other hidden
When the first input receives focus, the 2nd input will be shown
When the 2nd input receives focus, it remains shown
When both inputs lose focus, and none of them are focused, the 2nd input will be hidden
However, it seems that when I simply try to do something like $('body')[0].outerHeight in my blur event handler, the second input never receives the click or focus. Since it's too much to simply write down, here's a link to JSFiddle that demonstrates the issue: http://jsfiddle.net/7K2Ha/3/
Note - it happens the same with plain JS
Can anybody explain why this happens and are there ways around this?
Firstly, you bind focus/blur events to all .jq inputs. And then you add next event handlers (which means, that all of them will be executed - including the first one, which removes the has-focus class).
Check this fiddle, line 20: fiddle here.
$('#input3').unbind('blur').on('blur', //...
The problem is not specific to offsetHeight, and I believe if you comment out that line you don't get the effect simply because the browser will not call an empty function block for performance/optimizer reasons.
It seems to come down to the execution order of the events (especially problematic when more than one jquery event of the same type is wired up).
Check out what happens when you wire up the blur/mouseover/etc. events that all $('input.jq') elements have in common at the very end: http://jsfiddle.net/7K2Ha/6/
All I did was move the topmost javascript block to the bottom, and suddenly it works. In that case, the focus event seems to occur before the blur event. Notice how before the focus would never be called.
As a more robust solution, I'd only work with one event that covers both:
$('input.jq')
.on('focus', function() {
// get focus'ed elements parent
var thisParent = $(this).parent('div')
thisParent.addClass('has-focus');
// get all other parents
var otherParents = $('input.jq').parent('div').not(thisParent);
otherParents.removeClass('has-focus');
})
See updated JSfiddle.

How to get a Javascript routine to NOT act if it's in a text box?

I've got a simple page, and in that page runs a simple jquery keypress routine to catch clicks of the numbers 1 to 9 (has to be that to pass RNIB accessibility test).
And in that page is a form, which can have numbers entered as part of a postcode.
http://find.talking-newspapers.co.uk/result.php?addressInput=kingston
Scroll to the bottom, try typing 8 or 9 for example. The text is entered, but it also acts on the keypress. Expected, but not good.
I'm aware of various things like document.getElementById, but I can't figure out how to put these together to ensure that while the cursor is in the text input box, it doesn't act out the keypress catcher.
The target property of the event object (the parameter to the handler function) will tell you which element actually generated the event.
You need to check whether e.target is an <input> element, like this:
if ($(e.target).is(':input'))
return;

Trigger JavaScript event when using Google auto fill on Firefox

I have a form, in which I am disabling the submit button until an user has typed in all the mandatory fields. I was initially using onkeyup to keep a tab on the mandatory fields and enable the button when all the mandatory fields are filled.
But I had users complaining that they filled in the form using AutoFill button on the Google toolbar and the submit button was still disabled.
I fixed this problem in IE by calling the onpropertychange event for each input element and it worked nicely.
But in Firefox, I couldn't find an event which will get triggered when the Google autofill button is clicked.
Help much appreciated.
Thanks for your answers. I had to respond quickly to this issue hence I used the 'setTimeOut()' function to check for mandatory fields and enable the submit button.
$().ready(function() {
CheckRequiredFields();
timeOutRtn = setTimeout("AutoMonitorMandatoryField()", "3000");
});
function AutoMonitorMandatoryField() {
if ($("#btnSave").attr("disabled")) {
CheckRequiredFields();
timeOutRtn = setTimeout("AutoMonitorMandatoryField()", "3000");
}
}
crescentfresh - I will look into the DOMAttrModified event and see if I can get it to work for me.Thanks
Judging from this google toolbar support thread, it seems autofill is not only a huge PITA for developers, but also very difficult to turn off. As of Aug 09 google claims it will honor the autocomplete="off" attribute on the containing form but as of today this feature does not seem to be released yet.
You used to be able to give your input elements non-sensical names (eg name="xx_Address_32423423") to confuse autofill (and thereby effectively disable it), but they've made autofill more "intelligent" by looking at substrings within your element names in order to determine if the field can be autofilled or not (again, judging from complaints in that thread).
In your case, you may as well roll with the punches and find an equivalent for onpropertychange for Firefox. Have a look at the DOMAttrModified event. Specifially, try checking the event.attrName property to see if the value has been changed by autofill:
function realOnChange(event) {
var attrName = event.propertyName || event.attrName;
if(attrName === 'value') {
// etc
}
}
The check for event.propertyName is to stay compatible with your current onpropertychange implementation (if that is even possible).
There's no need to add complex setTimeOut nor setInterval.
Just catch the "change" event of any refillable textbox of the form, go through every refillable field and if it's not empty hide the label

Is there any way to reset the cursor to the beginning of a textbox when it loses focus?

I have some input[type="text"] textboxes on my form that are sometimes not wide enough for the content that gets typed into them.
Currently, if a user types more text that fits (but is under maxlength), the textbox scrolls as you would expect. When they tab out of the textbox, the textbox stays scrolled to the "end" of what they typed.
I am trying to find a way to reset the textbox to the beginning when a user tabs out of the field.
I looked at the suggestion here:
Set keyboard caret position in html textbox
...but it does not work in an onblur event because the solution actually causes the textbox to get focus again briefly before bluring again which causes an infinite loop.
Before I give up, does anyone have any solution to resetting the cursor to the beginning of a textbox in an onblur event?
In Internet Explorer, you can set the scrollLeft property. That doesn't work in Firefox, but if you reset the value to itself, the it will scroll back to the beginning. Apparently either method works in Chrome. I don't have any other browsers available to test at the moment.
$("input[type=text]").blur(function() {
this.value = this.value; // Firefox
this.scrollLeft = 0; // Internet Explorer
});
I used jQuery because that's what I'm used to, but you can adapt it to whatever framework/method you use for events.

Categories

Resources