How to make a custom web component focusable? - html

I'm writing a custom web component that is meant to be interactive. How can I tell the browser that this custom component should receive focus?
I wish that my custom element…
could be focused (by tab-navigation);
could receive keypresses when focused;
could be matched by :focus pseudo-selector.
I'm not using any external library, just plain HTML5 APIs.

Based on this demo that I found in this question, I have this answer:
Just add the tabindex attribute to the elements you want to be focusable.
// Add this to createdCallback function:
if (!this.hasAttribute('tabindex')) {
// Choose one of the following lines (but not both):
this.setAttribute('tabindex', 0);
this.tabIndex = 0;
}
// The browser automatically syncs tabindex attribute with .tabIndex property.
Clicking on the element will give it focus. Pressing tab will work. Using :focus in CSS will also work. keydown and keyup events work, although keypress doesn't (but it's deprecated anyway). Tested on Chrome 44 and Firefox 40.
Also note that this.tabIndex returns -1 even if the HTML attribute is missing, but this has a different behavior than setting tabindex="1":
<foo></foo>: No tabindex attribute, the element is not focusable.
<foo tabindex="-1"></foo>: The element is not reachable through tab-navigation, but it is still focusable by clicking.
References:
http://www.w3.org/TR/html5/editing.html#sequential-focus-navigation-and-the-tabindex-attribute
https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute
https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
https://github.com/whatwg/html/issues/113

#Denilson, I would like to provide you with some more information.
As you said, this.tabIndex = 0 works when your webcomponent contains no focusable elements. If it does, it gets more complicated.
For example, if your component contains one or more inputs, then first the "whole" component gets focus, and only later, when tabbing, each inner inputs get focus, one by one. This is usually not what you want. Usually, when the component gets focus this should mean its first input gets focus immediately.
Also, there is a reverse tabbing problem. If your first input has focus and you press SHIFT-TAB, then the "whole" component gets focus, and you are forced to press SHIFT-TAB twice to move to the previous element.
I found this to solve all focus and tabbing problems:
// At first, the component may get focus and accept tabbing.
createdCallback = function () { this.tabIndex = 0; }
// When the component gets focus, pass focus to the first inner element.
// Then make tabindex -1 so that the component may still get focus, but does NOT accept tabbing.
focus = function (e) { firstFocusableInnerElement.focus(); this.tabIndex = -1; }
// When we completely left the component, then component may accept tabbing again.
blur = function (e) { this.tabIndex = 0; }
Note: As of now (Sep 2015) if an inner element gets focus, then the "whole" element is not matched by the :focus pseudo-selector (tested only in Chrome). If find this behavior to be just plain wrong. The focus event was fired, and the blur event was not. So the element should have focus, right? I hope they change this in the future.

Short answer: delegatesFocus is what you need here, not tabindex.
Details:
Assuming that you have interactive elements inside the shadow DOM, there is no satisfying way to make the component programmatically focusable with tabindex:
if you set it to 0 you add the host element to the tab sequence ("sequential keyboard navigation") and you have an extra tab stop
if you set it to -1 you remove not only the host element but any interactive element inside its shadow DOM from the tab sequence, so the whole thing becomes inaccessible for keyboard users
There's a web component API just for this: ShadowRoot.delegatesFocus, see here. Set this to true and you'll get:
calling .focus() on the host or clicking on any non focusable part of the component focuses the first focusable element in the shadow DOM
:focus styles are applied to the host in addition to the focused element within
tab sequence is unchanged (it should already work the way you want)
It's supported since shadow DOM v1.

One very pragmatic approach I use, if possible and suitable, is just to put a <button type='button'> around my custom element.
This maybe does not fit as solution for you, I mention it anyway for others stepping into this question / problem.
It handles all focus matters, including a focus rectangle an so on.
To tame a <button> is less work than it seems (think especially about the line-height the button changes)

Related

Lost focus on many elements (like filters) in application

I must adapt to application to wcag focus requirements.
My application works on jsf/primefaces and I have problem with focus on many elements.
For example unexpanded filters contains ui-helper-hidden-accessible class (which is invisible until I click filter to expand) and I cant focus on unexpanded filter name to expand it by keyboard because (I think) ui-helper-hidden-accessible which size is 1x1px receive focus.
I want to span element inside filter div or div with ui-selectcheckboxmenu-trigger class will receive focus instead of unexpanded ui-helper-hidden-accessible.
Do You know how to do it?
Second example is primeface's menuItems - they also not receive focus. The only solution I know is to replace the elements to elements without ui-helper-hidden-accessible class, but unfortunately I can't use this soultion
<nf:author id="filter-selected-authors"
value="#{applicationsBean.dataModel.filter.selectedAuthors}"
converter="#{userConverter}"
completeMethod="#{applicationsBean.usersAutoComplete}"
componentsToUpdate=":datatableForm:applicationTable"
dataTableModel="#{applicationsBean.dataModel}"
oncompleteJs="dynamicScroll()"/>

self disable button, accessibility

is there a way to don't read 'unavailable' or 'dimmed' in a self-disable-button?
See the example Example
var saveBtn = document.getElementById("saveBtn");
var helper = document.getElementById("helper");
var content = document.getElementById("content");
saveBtn.onclick = function(e) {
saveBtn.setAttribute("disabled", "disabled");
saveBtn.setAttribute("aria-disabled", true);
content.innerHTML = 'Lorem input a lot of stuffs';
helper.innerHTML = "Content added, please read it";
setTimeout(function(){
helper.innerHTML = "";
saveBtn.removeAttribute("disabled");
saveBtn.setAttribute("aria-disabled", false);
}, 5000);
};
Voice only says: 'Content added, please read it'
NVDA says: 'Content added, please read it. Unavailable'
I know that it is happening because the button still having focus. But, I need to find a solution for that because I can't modify the current behavior of my page.
I'm using the html helper to inform transitions as you can see https://stackoverflow.com/a/38137593/3438124.
Sorry for ugly code, this is only to simulate my real behavior.
Thank you guys!
You will need to move focus to the new element. Give it tabindex=0 and then use focus().
aria-live is for when you want to have the screen reader speak changes for the content area with the aria-live attribute. Since you use it on a sibling div, you are missing the opportunity for it to just do what it does. Alternative, you can skip the tabindex/focus() approach and just put aria-live on a container for your new content.
Also, you can ditch the aria-disabled and just lean on disabled for the button. aria-disabled is for elements that otherwise do not support disabled.
You answered your own question when you said "I know that it is happening because the button still having focus" and then aardrian pointed it out explicitly when he said "You will need to move focus to the new element".
Keep in mind, though, that whether the focus remains on a disabled object is totally up to the user agent (browser). Some browsers leave the focus on the disabled object and others move it to the parent.
aardrian's comment about using tabindex was if you wanted to move the focus to an object that is not normally focusable (ie, if it's an element you can't normally TAB to). So if you want to move the focus to some simple text (just to get it off the button), then I'd tweak aardrian's suggestion and use tabindex='-1' instead of tabindex='0'. That will allow you to call focus() on that element but won't allow the user to TAB to it.
If you want to move the focus to another button on the page, or some other element that can naturally receive focus (checkbox, input field, etc), then you don't need tabindex.
If you end up moving the focus to the text that was just added, then you don't really need aria-live because the screen reader will read the text that you just focused to. But that's only relative to the example you posted, which I understand is just a sample to show the problem. Your real app might not be adding text.
And I want to second aardrian's recommendation of not setting aria-disabled when you're already using the disabled property. It's superfluous. The aria-disabled property is for when you're simulating a disabled object.
How the text is read varies reader to reader. None is right or wrong. If you want the text being read out to be same and consistent across the readers..Do a little workaround. Add an aria-label="desired text" to your button. This will override anything that is present inside the button tag.
eg
<button aria-label="desired text button">This text will be ignored</button>
Nvda will read the button as "desired text button". Text inside button is ignored. Now you can handle(add/remove) the disabled and aria-label attribute with JS.
In the context of your question you can try:
Instead of:
helper.innerHTML = "Content added, please read it";
Try using:
saveBtn.setAttribute("aria-label", "Content added, please read it.");
setTimeout(function(){
saveBtn.removeAttribute("aria-label");}, 5000);
Then the text will be same across the readers.

Focus is changed when inspecting element with CTRL+SHIFT+C

When I want to inspect an element in the browser with either CTRL-SHIFT-C or by clicking , the current focus is changed, possibly leading to elements that was previously visible not being visible any longer.
Is there some way of avoiding that?
You can select the element you need focus on in the Elements panel. Then find the :hov toggle (As of V49 iirc, 48 may still be a thumb-pin icon) which will open a menu for toggling given element states. From there, just select the one you need applied (focus in this case.) That will force the given state even if focus is somewhere else.

HTML: Same Page Anchoring and using Tab Key

I am using a link to jump to the content section of the page. It works fine; however, in IE and Chrome, after the jump if I press Tab it goes back to .
link : Skip to Content
Content Location <a id="anchortext" class='hidden'>Content</a>
Any Idea?
I suspect it has to do with the tab indexes of existing html elements on the page - clicking on an anchor tag as the one you specify above will take you down to the relevant section, but then pressing tab will take you to the first available tab stop (usually a link or form input item), which could very easily be back at the top of the page.
If you refresh the page and press tab once, you'll be taken to the first tab index enabled element of the page - I'm guessing that'll probably be the same section you were being taken to in your original question...
This appears to be a matter of different handling of internal links in browsers, and seems to fall into the category of behavior not defined in specifications, hence browser-dependent.
When you have focused on a link (usually, with tabbing) and hit enter to follow the link, browsers may or may not retain the focus. You can see the difference in behavior by using a CSS rule like :focus { background: yellow; }.
If the focus is retained (which is somewhat illogical, as the focused element may well be out of sight), a tab will take you to the next focusable element on the page (“next” in the sense of tabbing order).
If focus is lost, it may be treated as giving focus to the entire page (an IE oddity), or as having no focus. Either way, hitting tab will take you to the first focusable element on the page (as per the tabbing order).
I’m afraid there’s nothing you can do on a page to change this. It’s between the user and the browser.
AFAIK there's no way of doing that using only html.
So, i made a javascript script using jquery.
When the user clicks on the jump item, i look for the next link or the next link inside a specific element and put a focus on it.
This solves my problem with Chrome (>25) and IE (>7) and of course Firefox does the excelent job of interpret the tab action correctly.
$("#jumpToMenu").on("keydown", function(e) {
var keyCode = e.keyCode || e.which;
if(keyCode == 13) {
e.preventDefault();
e.stopPropagation();
$("#myMenuToJump").find('a:first').focus();
}
}).on("click", function(e) {
e.preventDefault();
e.stopPropagation();
$("#myMenuToJump").find('a:first').focus();
});
Hope this helps...

Disable events triggered on HTML <SELECT> control

Is there a way to capture the events triggered on HTML controls before they are forwarded for default (generic) handling by the control itself. In my case, I want to prevent a element dropdown to open when a user clicks on the control. e.g. On this user click, OnClick() event gets fired and is handled by the default control which open the dropdown. I want to stop this from happening.
Can I attach a custom function to this event and redirect the event handling to this one instead of the default code that opens the dropdown?
Thanks
onclick,onmousedown and onmouseup will not help you to prevent the selectbox from opening. I'm not asking why you want to do that, but if you really can't use any other solution, like for example (changing selectbox to the readonly inputbox), then, you can try the next solution.
One way to prevent the box from opening, is to create an overlay container, which will block the the focusable area of the select. This can be achived by placing the div after the selectbox and givving it the sizes and the position of the selectbox.
<div style="position:relative;">
<select style="width:100px;height:30px">
<option>hello</option>
</select>
<div style="position:absolute;
left:0;
top:0;
width:100px;
height:30px;
z-index:2;
background-color:black;
opacity:0;filter:Alpha(Opacity='0');"
></div>
</div>
Event then, it will work only for IE >= 7. Not for IE6, cause selectboxes in IE6 are strange( maybe you can try to fix IE6 with some iframe hack);
Fairly old question with some good suggestions, but none seem to directly answer the original question. In case anybody out there is wondering, I believe the OP was wanting to keep the visual appearance of the system/browser select element, but use his own custom drop-down menu instead of the system/browser drop-down menu.
In this case, the onclick event will occur too late for you to stop the actual drop-down menu from displaying. What you want to do is bind to the mousedown event, and prevent the event from propagating to the default behavior:
document.getElementById('my_select_id').onmousedown = function(event) {
// ... do something here...perhaps display your own custom menu, an advanced selection chooser, focus another element, display a message, or some other custom handling.
event.preventDefault(); // This prevents the drop-down menu from displaying
}
Notes:
Replacing the drop-down with a custom-designed element (as suggested by others) isn't always an option. In some cases, you'll end up either having to completely omit default/system drop-downs from your site (in favor of a custom-designed element), or you have to live with a mismatch in visual appearance due to browser/system/theme differences (unless you feel like designing the custom element to match every conceivable visual aesthetic/theme.)
Disabling the drop-down will not work, as it will prevent the event handlers from firing.
Using optgroups will still allow the drop-down menu to be displayed.
Replacing the drop-down with an empty version will still display an empty drop-down menu.
This is the answer I gave on another, similar question.
This works great for me in IE and Chrome, there's no flicker or anything:
html
<select id="MySelect"><option>Hello</option></select>
js
MySelect.onmousedown = function ()
{
window.setTimeout(function ()
{
//- An immediate blur, then refocus stops the options from being displayed
this.blur();
this.focus();
//- so now we run our custom function
runOtherFunctionInstead();
},0);
}
Make sure the js runs after the select element has been parse by placing it in an onload or ondocumentready or a script block after the select element. Haven't tried it in Firefox or Opera. Assumedly it would work in Safari, though.
EDIT
As suggested in the comments, the popup will still appear for a double click in IE (all versions). This is due to a bug where the mousedown event doesn't fire for the second click (whoops). You can quickly hide the options again by using the blur, focus method in the ondblclick event and if this method works in Firefox and Safari, I still think it's the best solution considering most people don't double click select boxes.
you need to set selectbox to be onload disabled: disabled="disabled"