Simulating a tab keypress using JavaScript
Asked Answered
C

5

22

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?

Chevet answered 31/3, 2009 at 19:7 Comment(5)
What do you want to invoke? Are you trying to move them to the next input?Holsworth
Yes. But the next 'input' is not necessarily an input or other naturally-tabstopped element. Nor is it necessarily unnaturally-tabstopped (i.e., $("[tabindex]") ). Physically pressing tab (or shift+tab) does exactly what I want...Chevet
Thanks for this. I was only interested in the first code example of your question, which really helped :)Grenada
Came across that issue too, Would love to know the list of those untrusted eventsLacedaemon
Related: #1602093 The accepted answer mentions ...firing an event doesn't trigger the default result of the user action, for security reasons...Lacedaemon
C
4

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.

Chevet answered 7/7, 2009 at 18:17 Comment(0)
P
3

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.

Pittel answered 25/6, 2009 at 22:10 Comment(2)
The thing is, some of my focusable UI elements are not form elements, because they are custom widgets.Chevet
For jquery users replace the first line of this function with: var fields = $('form')[0]; To loop through the form elements.Farris
C
1

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.

Chevet answered 1/4, 2009 at 18:2 Comment(1)
lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2008-December/… brought up an interesting point: "But then what if the user agent doesn't do things using a cycle but instead uses directional focus management, like many phones?"Chevet
B
1

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;
});
Buckjumper answered 11/9, 2013 at 12:10 Comment(0)
S
0

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')
Salcedo answered 23/8, 2009 at 7:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.