We currently have a telerik datepicker (and timepicker) displayed inside a bootstrap modal, and while it works (mostly) fine, I've discovered that accessibility keyboard controls (enabled via setting EnableAriaSupport and EnableKeyboardNavigation to True) do not work. The controls work fine in context of a datepicker outside of these modals.
Upon further investigation, it seems as though the telerik process is adding a new div to the dom when we open the datepicker calendar, which presumably is firing some low-level bootstrap event or process that I'm unaware of to force focus back to the modal... and in this case away from the datepicker that's appearing over it.
Does anyone know of a way to prioritize keyboard control focus to remain on the added datepicker calendar that appears instead of reverting back to the top of the modal?
Things I've tried include:
- Setting the modal's data-keyboard and data-focus properties to false (which did nothing except disabling people to press escape to close the modal)
- setting the date-picker's z-index arbitrarily high (despite it already rendering on top of the modal to begin with
- setting some properties for bootstrap-adjacent things in the off chance that they might work (such as Vue.JS). These, predictably, did nothing.
- fiddled with some raw JS events that might have impacted the process. These did not get hit upon loading the datepicker when test breakpoints were added to them
- setting a hidden field on the datepicker's sub-controls with the ID "hasControlOnModal" to True. This was preexisting code from the 2009 version of the datepicker that I'm currently trying to get away from, and doesn't seem to do anything as far as I can tell.
- confirming in VB code that the controls in and out of the modal are built the same way (they run through the same code)
- Tried to catch the calendar opening and manually force focus in JS via the OnPopupOpening event (this doesn't work as the documentation points out this event is just BEFORE the calendar loads into the dom... I need it to be there so I can use jquery's .focus() on it.
It's worth noting that I do not have the option to swap away from the current way we build modals and use a telerik modal at this time.
The datepicker, opened with keyboard controls, as it appears outside
the modal (note the black box around the calendar, denoting that the
control is in-focus of the keyboard and is usable via arrow key
navigation:
The
modal with a datepicker attached to it. The focus is on the invisible
"title" of the modal, which announces to screen readers that there's a
modal open. hitting "tab" here shifts the focus to the "close" button.
Tabbing
down to the datepicker works as intended, and upon hitting "enter" on
the open calendar button, you're presented with this screen:
Notice that the calendar does not have the focused black box around it, as the focus has shifted back to the top of the bootstrap modal. Keyboard controls do not work. Hitting "tab" from here will once again move the focus to the modal's "close" button. There is no way for me to tab into the calendar control.
Hi Ben,
I spent some time playing with the reported scenario with the following setup:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" %> <!DOCTYPE html> <html lang="en-us" xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>test</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script> </head> <body> <form id="form1" runat="server"> <telerik:RadScriptManager ID="RadScriptManager1" runat="server"></telerik:RadScriptManager> <!-- Button trigger modal --> <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal"> Launch demo modal </button> <!-- Modal --> <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="exampleModalLabel">Modal title</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <telerik:RadDatePicker ID="RadDatePicker2" runat="server" EnableAriaSupport="true" Calendar-EnableNavigationAnimation="false" ShowAnimation-Duration="0" EnableKeyboardNavigation="true" ClientEvents-OnPopupOpening="OnPopupOpening"> <Calendar runat="server" EnableKeyboardNavigation="true"></Calendar> </telerik:RadDatePicker> <script> function OnPopupOpening(sender, args) { setTimeout(function () { debugger document.activeElement.setAttribute("tabindex", "0"); document.activeElement.blur(); args.get_popupControl().CurrentViews[0].DomTable.focus(); args.get_popupControl().CurrentViews[0].DomTable.tabIndex = 0; args.get_popupControl().get_element().style.border = "1px solid red"; // console.log(document.activeElement) }, 300); } </script> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> <button type="button" class="btn btn-primary">Save changes</button> </div> </div> </div> </div> </form> </body> </html>
where the
args.get_popupControl().CurrentViews[0].DomTable.tabIndex = 0;args.get_popupControl().CurrentViews[0].DomTable.focus();
lines are taken from the source code of the showPopup: function(x, y) {
...
if ((this._calendar._enableKeyboardNavigation) && (!this._calendar._enableMultiSelect)) {this._calendar.CurrentViews[0].DomTable.tabIndex = 0;
this._calendar.CurrentViews[0].DomTable.focus();
}
}
to set the focus on the calendar table when the datepicker/timepicker popup gets opened.
The interesting part is that if I comment out the debugger in the example above the code focus the calendar in the dropdown (as you can see in the attached video), but if the debugger is commented the Bootstrap modal continues to steal the focus.
My research and conclusion show that the bootstrap modal steals the focus from the elements outside it as shown in my video demonstrating my test https://www.youtube.com/watch?v=3i-4fqzneNU and also noted in these online forums:
With regards to this, the tweak to stop this Boostrap behavior should be made in the Bootstrap source code but not in the Telerik one. This is a default Boostrap popup behavior and so far I am not aware of a working solution to stop it. What you can do as a workaround is to test with the Telerik Modal RadWindow, e.g.
<style> .RadCalendarPopup { z-index: 100002 !important; } </style> <telerik:RadWindow RenderMode="Lightweight" ID="modalPopup" runat="server" Width="360px" Height="365px" Modal="true" OffsetElementID="main" OnClientShow="setCustomPosition" Style="z-index: 100001;"> <ContentTemplate> <telerik:RadDatePicker ID="RadDatePicker2" runat="server" EnableAriaSupport="true" Calendar-EnableNavigationAnimation="false" ShowAnimation-Duration="0" EnableKeyboardNavigation="true"> <Calendar Style="z-index: 10000;" runat="server" EnableKeyboardNavigation="true"></Calendar> </telerik:RadDatePicker> </ContentTemplate> </telerik:RadWindow>
A note about this point: Tried to catch the calendar opening and manually force focus in JS via the OnPopupOpening event (this doesn't work as the documentation points out this event is just BEFORE the calendar loads into the dom... I need it to be there so I can use jquery's .focus() on it.
You just need to set a timeout and you can grab the calendar popup in the OnPopupOpening event as shown in my example in the previous post.