How do I prevent scrolling with arrow keys but NOT the mouse? - javascript

Since I'm using jQuery, any solution via that would work too. Ideally, I'd like to know both, though.
I already have the arrow keys bound to another function on my page (using jQuery), but having them cause the page to scroll in addition to that, causes me problems.
I may have known this at one time, but I don't remember it anymore.

Adding document level keypress handler does the trick!
var ar=new Array(33,34,35,36,37,38,39,40);
$(document).keydown(function(e) {
var key = e.which;
//console.log(key);
//if(key==35 || key == 36 || key == 37 || key == 39)
if($.inArray(key,ar) > -1) {
e.preventDefault();
return false;
}
return true;
});

Under some circumstances, eg. when you don't actualy have an element you focus on, just some area you had to click, you might not have too much control over the handler and preventing the event at the global level can be a little flaky at best (as I found out the hard way).
The simplest solution in those cases is to bind on the click even of the control button and focus on a empty input element which you position -9000px to the left.
You can then reliably block the event via keydown and also dont have to worry about blocking default behavior or other global listeners since the default behavior on the input element will just move the cursor to the left and right.

If you add a document level keypress handler it will prevent normal scroll on the page at any time, not only when your element has the focus, this might be an undesired effect.

Related

Tab cycling back to first tabbable DOM element instead of address bar

I have a tree component at the end of the DOM that handles keydown events so that the tree is navigable via arrow keys. I don't have any cases that respond to a "Tab" press, but the default behavior seems to send the focus to the first tabbable element in the DOM, rather than shifting focus to the browser.
I am unsure if this would qualify as a keyboard trap, as the focus is able to pass through all available DOM elements, and users can still access the browser via Ctrl+L/Cmd+L.
If I add a tabbable item after this tree component, such as a , shifting focus from the tree moves it to the button, and another Tab press will bring the focus into the browser.
Currently, I am not able to add a tabbable element to the end of the DOM that would make sense with the UI design. My main question: Does this looping back to the first element in the DOM from the last element introduce a Keyboard Trap concern? If so, is there any way I could get a Tab keypress to move the focus into the browser via Javascript?
Edit: This behavior was observed on Chrome on a Windows VM.
We're using jQuery to handle the keydown events, but removing the Tab case still cycles the focus back to the top:
$.on({
mousedown: {
//...handle mousedown event
},
keydown: function (e) {
var newFocus;
if (e.keyCode === keyCodes.Tab) {
// NOTE: tried removing this check entirely before, still cycles back to the first element rather than exiting focus to the browser
// Perhaps cycling to the first tabbable element is default jQuery keydown behavior?
e.preventDefault(); // Prevent tab nav from bubbling-up and triggering focusin
newFocus = e.shiftKey ?
getPreviousTabTarget() :
getNextTabTarget();
} else if (arrowKeyCodes.indexOf(e.keyCode) !== -1) {
//...handle arrow key keydown event
} else if (e.keyCode === keyCodes.Enter) {
//...handle Enter keydown event
} else if (e.keyCode === keyCodes.Space) {
//...handle Space keydown event
}
if (newFocus) {
newFocus.focus();
}
},
focusin: function (e) {
if (!currentItem) {
currentItem = jqElement.children()[0];
}
if (currentItem) {
currentItem.focus();
}
}
});
Edit 2: This example page from W3 showing a jQuery keydown/keyup event handler does pass the tab into the browser properly. There is definitely something I missed here. Thanks #QuentinC for the sanity check! And thanks to everyone for your responses.

Key event binding at table level

I have 2 tables on 1 page. I would like the user to be able to click the table and then use the arrow keys to navigate to previous and next. The only way I have been able to get the keyup events to fire in all browsers is by attaching them directly to the 'document'. I have wired up an event to add a 'grid-focused' class to the grid that is in focus and I tried using that as the selector on my events but I cannot get any event action then.
Event Binding
function attachInitEvents() {
if ($self.config.pageHotKeysEnabled) {
var keyNav = function(e) {
if (e.keyCode == 37 && paging.hasPrevious) { // left
$self._log.info('Left arrow key pressed.');
fetchData('prev');
}
if (e.keyCode == 39 && paging.hasNext) { //right
$self._log.info('Right arrow key pressed.');
fetchData('next');
}
}
var $doc = $(document);
// hot key support
$doc.off('keyup');
$doc.on('keyup', keyNav);
}
}
I am having another issue, where I am doing $doc.off('keyup'). I think this is part of the problem as it is unbinding all keyup events, what I really want is for it to just unbind the event I am trying to attach if it exists. In jQuery docs it technically says I should be using $doc.off('keyup', keyNav); to unbind it, but it does not unbind the event and I get multiples of it.
So core questions are,
Can I trigger a KeyUp event without binding to the document and can I do it from the table level?
How can I properly make sure I am not rebinding over and over again? Now, if this page has 2 tables, I guess I would expect there
to be 2 events one for each table and then the .grid-focused class
would be what allows the event to trigger or not?
Here is a Fiddle: http://jsfiddle.net/93fp293w/
Basically, I have no input boxes it is just spans, I am basically making it easier for them to page through all the data by just using the arrow keys. I believe the fiddler is pretty accurate to the situation. I feel what I am asking is not possible since I wont have an actual control in focus and if that is the case I can accept that but I guess I am hoping there is a work around.
The problem with attaching keyup is that, the parent div has to be mutable(contenteditable). else you have to attach to the document.
I have updated fiddle, might be a hack let me know if this works for you ?
http://jsfiddle.net/puneethp/qfpyeydg/3/
$(document).on("keyup", function (e) {
e.target = $(".focus")[0];
..
}
if you have only one column focused at a time, my previous fiddle will still work.

How do I get the actual event keydown target of an element inside a polymer component?

The code to prevent backspace from navigating back usually takes something like this approach, where the window keydown event is blocked for backspace, except on a small set of known element types like Input, TextArea, and so on.
This doesn't quite work for elements inside polymer custom elements, because the keydown event target type is the type of the custom element, not the actual element that got the keydown. Each custom element's type is different. This makes blocking backspace by target element type untenable.
Is there a way to know the type of the actual element, within the polymer element, that got the keypress? Or is there a better way altogether?
Whenever possible, it's good to try to engineer your project to avoid breaking encapsulation. This is the reason the event.target is adjusted when you cross shadow boundaries.
However, the event.path property is an escape-hatch that contains an array of all the elements that have seen the event and should allow you to solve this problem.
Get element from event: DIV, INPUT, TEXTAREA ...
e.target.nodeName
One way is to dig down into the custom element's shadowdom (and it's shadowdom), to get the true active element.
Something like this works on Chromium 36:
function getActiveElem(target) {
do {
if (target.shadowRoot != null) {
target = target.shadowRoot.activeElement;
}
} while(target.shadowRoot != null);
return target;
}
window.addEventListener("keydown", function(e) {
if (e.keyCode == 8) {
var preventKeyDown;
var d = getActiveElem(e.target); // Get the real active element
switch (d.tagName.toUpperCase()) {
case 'INPUT':
// more smarts here
preventKeyDown = false;
break;
// case TEXTAREA, et al.
}
// e.preventDefault() if preventKeyDown
}
}

toggle buttons using tab

It is simple. I have two buttons in a web page. They are sitting closely.
Currently I use mouse to click one of them then doing something.
Can we just use keyboard such as TAB?
Thanks.
Using tab to press buttons will completely break accessiblity on you websites and will effectively drive away any keyboard users from your website, it's an awful practice.
In any case, you might want to capture events for '*' using jquery:
$('*').keydown(function(event) {
if (event.keyCode == 9)
//stuff
})
Though I'd strongly recomend agaist doing this; never override what keys such as tab do, you'll create huge accessiblity issues.
[EDIT]:
Adding the event to document might be more efficient:
If key presses anywhere need to be caught (for example, to implement
global shortcut keys on a page), it is useful to attach this behavior
to the document object. Because of event bubbling, all key presses
will make their way up the DOM to the document object unless
explicitly stopped.
Source
jQuery would be perfect for this just bind the key to that button. something like this.
$('your selector here').keyup(function(e){
if(e.which == '9'){
//do your stuff here
}
});
I think 9 is the correct charcode for tab but you might need to check that. and make sure you do this inside of $(document).ready();
Yes, hit Tab until the button has a dotted border to indicate focus, then you can hit Enter or Space to click the button.
E.g. Button 2 has the focus here and pressing Space or Enter would "click" it:

In client-side JavaScript, is it possible to capture an event and fire it at a later stage?

I'm interested in doing something like this:
...
event.preventDefault();
...
el.dispatchEvent(event);
I tried this in Firefox, which threw an NS_ERROR_ILLEGAL_VALUE exception.
Is it possible to capture an event and fire it at a later stage?
For those that are interested, here's my high-level objective. I'm trying to determine when an underscore is typed into a textarea (i.e. shift + -). Unfortunately, Firefox reports the keyCode and charCode for this event are 0, the same value given to the tilde (shift + `) keystroke. To disambiguate, my idea is to capture the event, suppress its default behaviour, and "release" it on another textarea. I'd then inspect the value of this (hidden) textarea to determine which key was pressed.
Update: I'm using onkeydown, not onkeypress.
As far as I know, an event already in the queue cannot be "reused" because it cannot be "pulled out" of the queue. It's given to you, then to the next handler in line, and so on, but the native delegate is the same for all of them. So, you have to make a new one. Since you're saying you can't get all the data about the event out, that's a problem.
An easier trick may be to watch the textarea for change, and then delete the underscore when it appears in the text. If you want to maintain the cursor position, you can look here for a solution on how to exactly position the cursor (RonPK's response).
Out of curiosity, according to my test here, Firefox 4 reports the correct charCode and shift state. Is this a specific version/OS issue?
What's wrong with:
String.fromCharCode(event.keyCode);
?
E.g. within the event handler:
var character = String.fromCharCode(event.keyCode);
if (character === '_') {
// Do something.
}

Categories

Resources