I've got an HTML link, and I want to take some action when the user tabs away from it - but only if the user is tabbing forwards through the document, as opposed to backwards.
Is there a reliable cross-browser way to detect which way the user is tabbing through the document, or indeed if they're tabbing through the document at all? I'm binding to the blur event, but that doesn't necessarily mean that the user is tabbing.
I've had a look at inspecting the value of document.activeElement, or the hasFocus attribute of the previous focusable element in the source, but:
those seem like relatively recent additions, and thus might not be widely supported, and
I'm not sure they'll be inspectable when the blur event fires, as even if the user is tabbing, I don't think the next element will be focused yet.
You will have to handle keydown event on the link itself.
$("your link selector").keydown(function(evt){
if (evt.which === 9)
{
if(evt.shiftKey === true)
{
// user is tabbing backward
}
else
{
// User is tabbing forward
}
}
});
Check this JSFiddle example that detects forward and backward tabbing on a particular link.
Sidenote: You didn't specify jQuery tag on your question al though I provided jQuery code in my answer. Since you hold Javascript as well as jQuery badges I suppose it's going to be trivial for you to convert my code to pure Javascript.
As an alternative to a good solution from Robert, maybe you can leverage tabindex attribute? Set it for your html links and other “tabbable” items. Then check it in javascript.
Solution with tabindex: jsfiddle
Side effect: Elements will also react on mouse clicks. They will behave correctly. So this might be a good side effect or bad side effect depending on your needs.
Related
I'm working on keyboard accessibility. I have a flash object which sits inside a page, and to stop the focus getting trapped inside it, I've added tab listeners which talk through ExternalInterface to some JavaScript functions.
The JavaScript looks for the next available element with a tabIndex and calls focus() on it. So far so good. But if the plugin is the last tabbable item on the page (or first when reverse-tabbing), there is no element to switch to. Normally this would set focus to the browser window, so I'd like to keep that behaviour.
Hence my question: Is it possible to programatically give focus to the browser's chrome? Even better if I can mimic both forward and backward tabbing. Also I'd like to avoid adding extra tabbable components before/after the flash if possible, unless I can make them effectively invisible to both the mouse and keyboard.
Came across this in my own search for a similar answer. If you want to release the focus on the currently focused element, use document.activeElement; If you want a fallback in the off chance its not supported, use focus on the parent element of the document. This should support all known browsers, as far as I know:
var activeElement = document.activeElement;
if (activeElement) {
activeElement.blur();
} else if (document.parentElement) {
document.parentElement.focus();
} else {
window.focus();
}
Setting the focus to a dummy anchor with no text content, at the top of the document, seems to work:
document.getElementsByTagName('a')[0].focus();
http://jsfiddle.net/4NA5u/1/.
A third party script is being used on a site I work on that replaces a few instances of <a href=""> with <a>. The links still work thanks to another part of the script, but they are no longer treated as links by user agents.
I can restore them to the tabbed navigation order by adding tabindex="0" but how can I make assistive technologies announce them as links or include them in a list of all links on a page?
Would adding role="link" help at all?
I am pushing the third party to improve their script so that the href is left intact. But in the meantime how do I best repair the damage that's being done?
I can't add either the original href or something like href="#" back to the links as the third party code will no longer do what it does. I hope that they improve their code so that I can, but for now I need to make the link accessible without the 'href'.
To make a non-href <a> behave like an <a> (and be accessible), you'd have to add role=link, tabindex=0, style it to look like a real link, and add keyboard handler code to treat Return as a click.
role="link" isn't sufficient; a screenreader may report it as a link, but without tabindex="0" and appropriate visual styles, a sighted user won't be able to tab to it in the first place, and without a keyboard event handler, only mouse users will be able to click it. (Technically screenreader users typically have hotkeys to simulate a mouse click, but keyboard-only sighted users generally don't have that option, so don't rely on it.)
Alternatively, if (big if!) the crazy script you're using allows for it, you could try shimming a 'keyboard click source' (my terminology) <a> just inside the original one: so where you have:
<a>foo</a>
you replace it with:
<a><a class='shim' href="javascript:void(0)">foo</a></a>
(The class='shim' is only needed if you need to do the event stuff described later...)
You can do this in jQuery using something like: (borrowing from Jack's answer)
$("a:not([href])").wrapInner("<a class='shim' href='javascript:void(0)'></a>")
How this works is that the inner newly-added <a ...> has a href, so it is exposed as a link and is tabbable. More importantly, if a user tabs to it and presses return, the default A behavior converts that keyboard input into a click event. This specific A has a href that returns undefined/void(0), so no actual navigation happens, but the click event will still bubble up to the original A, which gets to act on it.
(This is a neat pattern for allowing some parent element - often a DIV or similar - to handle click events, adding a child tabbable A that can source click events from keyboard gives you UI that's both mouse and keyboard usable.)
The big caveat here is that it assumes that your original script doesn't care about the target of the event. If that script does check this, it will get confused when it sees click events coming from the shim A's rather than the original As. One way to get around this is to capture and re-raise the event, which can be fiddly, and may only work on recent browsers - eg using something like:
// 'shim' class used so we can do this:
$("a.shim").click(function(e) {
e.preventDefault();
e.stopPropagation();
// the following works if listener using jQuery or is setting onclick directly, otherwise...
// $(e.target).parent().click();.
// More general way to raise events; may need alternate for IE<9
var e2 = document.createEvent("UIEvents");
e2.initUIEvent("click", true, true, window, 1);
e.target.parentNode.dispatchEvent(e2)
});
Whilst it's not very pretty, you can get at all anchors without a href attribute like so, using jQuery;
$("a:not([href])")
You can then just set the href attribute on those links to "#" and that should make them work again as regular links.
Here's a working JSFiddle
Sorry to reply with a jQuery solution...but doing this in regular JavaScript would be much more verbose.
Another way would be to give the anchors a role and then select them that way:
$("a[role='link']")
I want to create a little WYSIWYG editor.
The idea:
First I want to add the feature to write and change text. So I add an onClick and onKeyBoard Listener to my div container. When I click the div I set a varaible named "focused" to true. When an key event is fired I check if focused is true. In case focus is false nothing will happen else the new charater will be added on the cursor's position.
My questions:
Is this the right way? I tried to check how other editors handle the text input but I wasnt able to get it.
In case this is the right way - how can I simulate a blinking cursor. In a textarea the cursor will blink but who about a div container? The cursor will hide immideatly after clicking.
I'm assuming you're doing this for fun/practice. If you're doing this for professional reason then I HIGHLY recommend you don't reinvent the wheel and use something like Ckeditor, tinyMCE or YUI.
That being said; you need to look into event handling. Specifically, for your question about focusing, you can look here. The way you're describing (setting a variable to true/false) seems like it is going to just run into problems. If you use the standard events attribute (as opposed to setting a "focus" variable onclick) you should define functions to execute and then set them as an onfocus/onblur attribute for the element you're listening to.
That is if you aren't using a javacript library like mootools, jquery, extJS, etc. If you're using one of those they likely have their own way of handling events, so you should search their respective documentation for how to implement event handlers.
One more note; you really should be using a textarea over a div (unless I'm misunderstanding and you just want to do something when a user focuses on your div). If you're using javascript only to completely reinvent a texteditor from a div; then your web page will not function without javascript. If you keep the text area; users could still type information in and you still get the benefit of grabbing text contents for form submits but using divs means your web page will just be rendered useless without javascript.
Forcing focus() on an element is easy, but after having debug issues, I realize trying to determine where is my focus gone is a lot harder.
The thing is I'm generating a modal window using jq.UI, and from time to time, while focus is supposed to be set on first input of the form included in the modal, cursor just disappears, to never show again unless I reload the page.
Is there a straightforward way to detect where my focus/cursor is?
You can see which element it's on by checking document.activeElement, for example:
alert(document.activeElement.innerHTML); //see the content to aid in IDing it
I'm not sure if the focus event bubbles, but if it does, you could try this:
jQuery('body').focus(function(e){ console.log(e.target); })
Scenario: I'm trying to intercept paste events inside a textarea/input text, and filter the content being pasted.
Webkit/IE are handled rather well, as I can attach code to the onpaste event, and then read from the clipboard what is being pasted. Plenty of examples around.
Gecko is trickier, because as far as I know it isn't possible to read the clipboard content on Firefox (unless somebody knows a workaround for that?)
I just use the input swap trick for that.
Opera is being annoying tho. I can trap CTRL+V and SHIFT+INS, but there's no onpaste event.
Not to mention any sort of clipboard interaction, apparently.
So, my question is:
Can I detect if the user clicked on paste in the context menu on Opera? Is there any other way to detect the event?
EDIT:
Thanks everybody for the answers - they all add a good input, even if there's no definitive solution.
Having to choose, I'll pick the only one that tried to address the original question, and that would probably work if it wasn't too much of an hack to even try.
Notes for those that have my same problem (input filtering):
it is possible to capture content being dragged: mouseup + setTimeout does the trick everywhere almost perfectly.
without flash, there is probably no solution bar polling. Even with flash, it's not a completely solid solution either. Too much effort to support 100% of the cases.
I ran into this last year. In short, no.
I ended up using an onchange handler and filtering the content after it's already been pasted into the text box.
You can intercept the paste with jQuery using the bind('paste', function() {});, compare string before and after pasting and apply your formatting.
The following was tested in IE7/FF3.6/Chrome/Safari 5
$("#textarea").bind('paste', function(e){
// Do whatever you needed to do with the code here.
});
Live Example http://jsfiddle.net/VSrTg/2/
Edit An approach would be something like this:
$("#textarea").bind('paste', function(e){
var oldText = this.value;
setTimeout(function() {
// Compare oldText to $("#textarea").val() and format accordingly.
}, 1000);
});
Edit 2 Given your revisions to your original post, if you're worried about the giant market share that is Opera, you're going to have to monitor the value of your textbox with a setInterval() and compare it against itself for changes.
Ultimately there will always be a way around your script, even the above example is susceptible to simply dragging text from another text box (or the address bar) into it without triggering the paste event defined above.
I would like to point out DOJO menu widget that is creating context menus perfectly in different browsers. http://www.dojotoolkit.org/reference-guide/dijit/Menu.html#dijit-menu
What you can do is that detect paste event in browsers that are supporting it and override context menu in browsers that are not supporting this event like opera.
Once you create your own context menu then you can add copy paste menu item or create context menu similar to the default using css.
Edited
Some browsers might not allow us to fetch clipboard content, in this case we can always revert back to flash for borrowing some of its features that are cross browser. See couple of links I posted in comments.
Its complete implementation might have more issues than anticipated but it is possible and we can always give it a try (I will for sure).
The answer to the question is a simple no. The main browsers that have no paste event are recent versions of Opera and Firefox 2. Given that there is no paste event, you need to find an alternative event or set of events to detect a paste from the context menu as it actually happens. You can add handlers for every event there is (I've done this) and you simply get nothing in the relevant browsers when a paste is triggered from the context menu by the user.
This only leaves polling the text input's value regularly, which is not the same thing. You could keep track of keypresses and observe in your polling code that the text input's value has changed by some means other than keyboard input and do a diff, but that's hacky and unreliable.
I use the setTimeout for paste events. But for context menu select nothing seems to work(as stated above). I bind a mousemove to the input's form which fires the update function. Then unbind/bind so they don't stack up.
This handles the context menu select and dragging value to input field.
If your form is small, say with only one input field and the mouse will not land on it after selecting from context menu, bind to the form's parent or document. Sure, it has to wait until the mouse moves but that is the general user action after selecting from context menu.
Works fine.