I have a list of offers, each of them with a delete button, that once clicked opens a modal with another 2 buttons: delete and cancel.
When the modal has opened, I want to be able to tab to these 2 buttons first, but at the moment tab continues with the other tabbable elements that are after the delete button in the row, eventually hitting the buttons after completing the tab round.
Is there a way of achieving that?
<section tabIndex="-1" role="dialog" onClick={onCancel}>
<section role="document" onClick={(e) => e.stopPropagation()}>
<Button tabIndex="0" secondary className={Styles.cancel} onClick={onCancel}>
{t("abort")}
</Button>
<Button tabIndex="0" secondary className={Styles.delete} onClick={onDelete}>
{t("deleteModal.CTA")}
</Button>
</section>
</section>
Tab continues wherever the focus is set. Your code already includes tabindex="-1" which allows to focus the dialog element after opening it, with .focus() in JavaScript.
If the dialog content includes semantic structures […] then it is advisable to add tabindex="-1" to a static element at the start of the content and initially focus that element.
In your case, the dialog is really simple, and it would be more advisable to focus the primary button instead of the dialog itself.
See Dialog (Modal) from the ARIA Authoring Practices Guide (APG)
Your dialog needs an accessible name. Usually a aria-labelledby attribute is used to refer to the dialog’s title element, if you don’t have a visual title you can use aria-label.
It is also important that you establish a focus trap in the dialog, meaning that one cannot leave the dialog by means of tab, but only by means of close. Usually the focus wraps around back to the dialog-element.
Browser support for the native <dialog> element recently got better and you might consider using that one.
Related
Popup-like components in Material-UI render on an invisible top overlay that is programmatically appended directly to body. For example, when a SelectField or DropDownMenu is opened:
<body>
<div id="app-container>
<div ...>
<!-- This is where the SelectField is rendered. -->
</div>
</div>
<div ...>
<!-- This div is: -->
<!-- (1) where the visible dropdown is rendered when open -->
<!-- (2) a full-screen overlay -->
<!-- (3) removed when the dropdown is closed -->
</div>
</body>
This mechanism is in place so that (1) the user can "click away" to close the open dropdown, (2) the user does not accidentally click on another control when attempting to close the open dropdown.
However, I need to allow clickaways to actually hit the underlying element, without being blocked by the overlay. This can be accomplished in the Popover component by using the useLayerForClickAway prop:
<Popover useLayerForClickAway={false} ... >
...
</Popover>
Unfortunately, no such option exists for SelectField or DropDownMenu (or seems to exist, it's not documented), and I need one of these instead of a Popover.
Moreover, I don't seem to find any option that allows to reliably identify the overlay element created by the dropdown, beside getting the last element in body immediately after the dropdown opened (which is hackish at best). Material-UI also doesn't seem to offer a way to manually override the inline styles of this overlay element.
So, what's the "appropriate" way to disable the invisible overlay, and allow clicks when the dropdown is open?
I am implementing accessibility for dropdown component, the special feature of my dropdown is it populates values in options menu only while opening dropdown,meaning it on the fly compiles the template and attaches to the dropdown box.
Dropdown HTML:
<div id="dropdown" ng-click="openDropdown()">
<div id="selectedValue" role="listbox" tabindex="0" aria-label="{{selectedVal}}" aria-owns="dropDownMenu">{{selectedVal}}</div>
</div>
DropDown Menu template(which gets compiled and polpulated after clicking dropdown above) :
<div id="dropDownMenu">
<li ng-click="selectItem()" role="option">item1</li>
<li ng-click="selectItem()" role="option">item2</li>
</div>
I am facing two problems
As my #dropdownMenu gets generated on click of #dropdown(dynamic template generation) jaws do not have access to #dropdownMenu when focus comes to #selectedValue so it doesn't announce the number of options etc as in case of a typical selectbox.
I am giving aria-label="{{selectedVal}}" for #selectedValue so on click of arrow keys javascript takes care of updating selectedVal even though #dropdownMenu is not open ,but changed value of selectedVal is not announced by jaws 16.0 ,it only announces it only first time as user tabs into it .Noted that this works fine in jaws 14.0 .
Looking forward for some solutions....
Adding aria-live=polite should fix this.
Is there a reason you're not using a standard select box and populating the option elements with your dynamic content? That would remove the need to update an aria property with the current option, as screenreaders will find it themselves. Also aria-label should be the name of the selectbox (or its purpose) not its selected option. If you were using a HTML select with options you could then remove the tabindex and aria-live as well, since native form inputs have full keyboard and screenreader support by default.
You should probably wait until the element is rendered and appeared in the DOM and only then set the focus to the first submenu item by using a native function .focus(). That will do the job.
But... Make sure that if the request takes too long and the user has already left somewhere else doing something else on the page, that in this case you don't steal his focus to get him back to the dropdown menu otherwise he might be annoyed.
Also instead of tabindex=0 for interactive elements (wherever you use ng-click) I would recommend that you use the actual native elements such as <a> or <button>. That way you ensure that the elements will be focusable both by keyboard but also visually, and react to ALL keyboard keys which the users are used to use and thus expect it to react such as SPACE or ENTER without needing you to implement it manually.
Really simple question here...note the code snippet first:
$('#gasmask').on('click', function(){
$('#gasmask').modal('show');
});
Clicking the element with an ID of gasmask causes the Semantic-UI modal to show, quite nicely. The issue is that after I click away, causing the modal to disappear, so the does the original element...
Before click:
<div class="content">
<img alt="Gasmask" class="fademein3 ui image" id="gasmask" src="https://s3.amazonaws.com/verumdesigns/gasmask.jpg" style="visibility: inherit; opacity: 1;">
</div>
After modal closes:
<div class="content">
</div>
It just straight up vanishes from the DOM. What's the dealio?
Semantic UI moves the modal contents around in the DOM, as you have figured out. I believe this is so that the modal gets the appropriate inherited styling to display as intended, rather than inherit whichever "component" embeds the modal.
There is an option called "detachable", which you could use like such:
$('.modal')
.modal({
detachable: false
})
.modal('show')
This would ensure that your model contents stay where they are. This has hardly ever been a good option for me, because the modal inherits the styling I use for the component that embeds the modal, which just mucks up the modal. Maybe there is a way to prevent that with CSS?
If CSS can't come to the rescue, then it's up to redesign your frontend.
For me, that meant turning the modal into a singleton component that I can instantiate from another component, passing whichever values are needed to display the modal. Obviously details on that will differ on whether you are using Riot, React, Angular, ...
Another option, if you only have one modal and you don't care where it lives in your DOM, is to id your element with the HTML id attribute. That way you don't care where it is relative to your component.
I'm thinking if there's a way to add ID attributes to 'accept' and 'cancel' buttons within a dialog?
<dialog id="myDialog" title="My Dialog"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="window.sizeToContent();"
buttons="accept,cancel"
buttonlabelaccept="Set Favourite"
buttonaccesskeyaccept="S"
ondialogaccept="return doSave();"
buttonlabelcancel="Cancel"
buttonaccesskeycancel="n"
ondialogcancel="return doCancel();">
I know I can add buttons like this:
<button
id="identifier"
class="dialog"
label="OK"
image="images/image.jpg"
disabled="true"
accesskey="t"/>
But can be this done inside a dialog context?
It can be done inside of a dialog context. But the code you've posted might not work as expected depending on what you expect to achieve.
The button you posted will not function as the accept button unless it has special attribute dlgtype="accept". Citing this article that talks about creating custom dialog buttons:
If you are not satisfied with the layout of predefined buttons in dialog, you can put explicit button elements in your XUL file and add a dlgtype attribute to them. Valid values for dlgtype are the six button types listed above.
<button
dlgtype="accept"
class="dialog"
label="OK"
image="images/image.jpg"
disabled="true"
accesskey="t"/>
This approach enables you to choose where the accept button is located, styling of it etc.
If the goal is to get an accept button somewhere in the code and manipulate its properties, then just use document.documentElement.getButton("accept"). Citing the same article:
// Disable the OK and Cancel btns
document.documentElement.getButton("accept").disabled = true;
document.documentElement.getButton("cancel").disabled = true;
Let me know if you wanted something different.
My store's collection pages have the quick-view feature where hovering over products images allow you to click 'Quick View' to open a modal dialog window with summary info about the products. This is done with the leanModal plugin.
See: http://froy.com/collections/beds-mattresses for example. Store is powered by Shopify.
The problem is, when the page initially loads, all the elements within the modal are loaded despite being hidden. This unnecessarily slows down the site. I'd like to load the contents within the modal only after the user clicks on 'Quick View.'
<div class="quick_shop"> <!--This is the modal trigger-->
<div class="quickview-text">Quick View</div>
</div>
<div class="modal">
<!--All contents of the modal dialog window go in this div-->
</div>
Script:
$('.quick_shop').leanModal(); //Calls the function on the modal trigger
(function($){$.fn.extend({leanModal:function(_1)...});})(jQuery);
//The above line of code is for leanModal. I didn't paste entire thing cause it's long
Any help would be much appreciated! I'm new and learning so this is all very exciting.
The purpose of the modal div is to have a precise view on one specific element.
In your case (on the webpage you provided), the modal is loaded for each element, which breaks the sense of what you're trying to achieve.
I've never used the leanModal plugin but I guess you could try doing the following:
When the "Quick view" is pressed, find the closest element with jQuery .closest() method by searching the element with class .quick_shop, THEN leanModal that single element and display it:
$(".quickview-text").on("click", function(e){ $(this).closest(".quick_shop").leanModal(); //Then display it with .show() if it's hidden by default });
Once the modal is closed you can delete the element instead of hiding it with the jQuery's remove() method.