Why does my screen reader not announce the selected tab when activated - accessibility

I'm working on making my existing tab component accessible, I'm basing my design off of the W3C's Example of Tabs with Manual Activation.
You can access my demo here
HTML
<div class="tab-container" lang="en">
<div class="tabs" role="tablist">
<button class="tab" aria-selected="true" href="#" role="tab" data-tab-name="tab1" tabindex="0">
<!-- tab name -->
</button>
<!-- more tabs -->
</div>
<div class="tab-content" data-name="tab1" role="tabpanel" tabindex="0">
<!-- tab panel content -->
</div>
<!-- more tab panels -->
</div>
JQuery
function getTabContent($tabContents, tabName) {
return $tabContents.filter('[data-name="' + tabName + '"]');
}
function setSelectedTab($tab) {
var tabName = $tab.data('tab-name'),
$tabSet = $tab.closest('.tabs'),
$tabContents = $tab.closest('.tab-container').find('.tab-content');
// update the tab indices and aria attributes
$tabSet.find('.tab').attr('aria-selected', 'false').attr('tabindex', '-1');
$tab.attr('aria-selected', 'true').removeAttr('tabindex');
$tabContents.addClass('hidden');
getTabContent($tabContents, tabName).removeClass('hidden');
}
function handleTabSelection(event) {
var $tab = $(event.target);
if ($tab.data('tab-name')) {
event.preventDefault();
setSelectedTab($tab);
$tab.focus();
}
}
// Our tab control needs to be used in many places on our site, we cannot guarantee that all devs will use unique IDs
// so we need to generate them here
function initTabs($tabContainer) {
var $tabList = $tabContainer.find('.tabs'),
$tabContents = $tabContainer.find('.tab-content'),
tabSetName = $tabList.data.name,
tabIdPrefix = 'tab-',
contentIdPrefix = 'tab-content-';
// add unique ids and labels
$tabList.children().each(function() {
var $tab = $(this),
tabName = $tab.data('tab-name'),
$tabContent = getTabContent($tabContents, tabName),
tabId = getUniqueId(tabIdPrefix + tabName),
contentId = getUniqueId(contentIdPrefix + tabName);
// add the unique id and associate the link with the content
$tab.attr('id', tabId).attr('aria-controls', contentId);
// add the unique id and use the link as the label for the content
$tabContent.attr('id', contentId).attr('aria-labelledby', tabId);
});
}
function getUniqueId(id, index) {
var newId = id;
if (index) {
newId += '--' + index;
index++;
} else {
index = 1;
}
if (document.getElementById(newId)) {
return getUniqueId(id, index);
}
return newId;
}
function handleKeyPress(event) {
var $tab = $(event.target);
if ($tab.is('.tab')) {
var keyCode = event.which,
$tab = $(event.target);
if (keyCode === 13 || keyCode === 32) {
// user pressed enter, or space
setSelectedTab($tab);
event.preventDefault();
} else if (keyCode === 37 || keyCode === 39) {
// the user pressed left or right
var $newTab = $tab[keyCode === 39 ? 'next' : 'prev']();
// move the focus
if ($newTab.length > 0) {
$newTab.focus();
}
event.preventDefault();
}
}
}
$('.tabs').click(handleTabSelection);
$('.tabs').keyup(handleKeyPress);
$('.tab-container').each(function() {
initTabs($(this));
});
A user can use the left and right keys to move focus within the tab list, and enter or space to select a tab.
When a user selects a tab however, the screen reader simply announces "selected" where on the W3C's example, it announces the tab name followed by "selected".
I'm testing using NVDA in Firefox and here are my steps to reproduce:
Set the focus on the "Nils Frahm" tab
Press TAB
You should hear "Agnes Obel tab two of three"
Press ENTER
You should hear "Agnes Obel tab selected tab two of three"
This is exactly what happens in the W3C's example, but in mine, the final step only reads "selected".
I've tried to match their example as closely as possible but I have yet to figure out how to get my example to announce the tab name when activated.
What could cause NVDA to skip reading the tab name once it is activated?

I discovered how to solve the problem, but as of yet, not why the problem exists.
When I add an after CSS rule on my selected tab, the screen reader starts reading the content when selected.
.tab[aria-selected="true"]::after {
content: '';
}
If I add the after tag to all tabs, the problem persists; it needs to only be on the selected element.
My guess is that this is fooling the screen reader into thinking that the content has changed, so it reads the new tab name.
Here is the working demo

Related

How to make a that when a buıtton pressed a text change in wordpress

I wanna a text change acording to how many times a button clicked?
Any one with a solution??
Here is a simple Javascript that will change based on the number of times clicked.
An important thing to remember though - session storage will set your value to 0 when the tab is closed, local storage does not.
https://www.w3schools.com/html/tryit.asp?filename=tryhtml5_webstorage_local_clickcount
<script>
function clickCounter() {
if (typeof(Storage) !== "undefined") {
if (localStorage.clickcount) {
localStorage.clickcount = Number(localStorage.clickcount)+1;
} else {
localStorage.clickcount = 1;
}
document.getElementById("result").innerHTML = localStorage.clickcount;
} else {
document.getElementById("result").innerHTML = "Sorry, your browser does not support web storage...";
}
}
</script>
<p><button onclick="clickCounter()" type="button">Click me!</button></p>
<p><span>You have clicked the button </span>
<span id="result"></span>
<span> time(s).</span></p>

fullcalendar when render resources, all-day lines don't lined up

Previous post which lead to this issue: Fullcalendar using resources as a function with select menu
Based on my previous post, I have an issue using fullcalendar 4. When I am using resources as a function, my all-day blocks do not line up with my scheduler time slots. You can see it in the picture.
Here's my resources function:
resources: function(fetchInfo, successCallback, failureCallback) {
// Filter resources by whether their id is in visibleResourceIds.
var filteredResources = [];
filteredResources = resourceData.filter(function(x) {
return visibleResourceIds.indexOf(x.id) !== -1;
});
successCallback(filteredResources);
},
Here's my toggleresource function:
// menu button/dropdown will trigger this function. Feed it resourceId.
function toggleResource(resourceId) {
visibleResourceIds = [];
//if select all... see if undefined from loading on initial load = true
if ((resourceId == '') || (resourceId === undefined)) {
$.map( resourceData, function( value, index ) {
visibleResourceIds.push(value.id);
});
}
var index = visibleResourceIds.indexOf(resourceId);
if (index !== -1) {
visibleResourceIds.splice(index, 1);
} else {
visibleResourceIds.push(resourceId);
}
calendar.refetchResources();
Other related code (when the menu changes, the resources of the selected menu item show only in fullcalendar):
var resourceData = [];
var visibleResourceIds = [];
$.getJSON('ajax_get_json.php?what=schedule_providers_at_location',
function(data) {
$.each(data, function(index) {
resourceData.push({
id: data[index].value,
title: data[index].text
});
});
});
$('#toggle_providers_calendar').change(function() {
toggleResource($('#toggle_providers_calendar').val());
});
The resources show/hide just fine based on the selected menu resource, but look at the allday blocks - they don't line up after the resources are refetched for some reason. They correct themselves as the user navigates the scheduler though!
UPDATE BELOW
After looking around it looks like when refetchevents is called, the class .fc-week loses the following css:
style="border-right-width: 1px; margin-right: 20px;"
Here's a full pic of the calendar on initial load:
After I click a one of the navigation arrow, the all-day lines meet up with the rest of the calendar times because that style is applied to .fc-week.
I don't have any special css applied to the calendar and I am not using any themes that would get rid of this: at least not that I see now.
Here's the html that houses the calendar:
<div class="portlet-body">
<div class='loader'></div>
<div class="row">
<div id="calendar_full" style="padding-left: 10px; padding-right: 15px;"></div>
</div>
</div>
In order to fix this, I can add this following line after the resources are refetched in my toggleResources function:
$('#calendar_full .fc-week').css('border-right-width', '1px').css('margin-right', '20px');
I am going to keep looking as to why this css disappears after the resources are refetched. I wonder if it could be a glitch?

How can I disable/hide the Dev Tools plugin in Kibana

I want to hide the Dev Tools menu item in Kibana, but according to their roadmap, that's not possible with their permission system nor will it be anytime soon. (see https://discuss.elastic.co/t/disable-hide-management-plugin-kibana-5/72763)
Kibana is inside an iFrame hosting a site on the container's domain.
I ended up using MutationObservers in JavaScript to watch for changes to the DOM inside the iFrame in order to hide the menus I didn't want non-admins to see. Solution written in AngularJS 1.2 and is known to work with Kibana 6.2 and 6.3. This will hide several "left side" menus as well as a bunch of Management sub-menus. You can use or modify the code to hide/show additional UI elements. Unfortunately, I had to rely on classes a lot since very few elements contained IDs I could reference.
I hope this at least helps you think of your own solution to managing Kibana display elements outside of their permission structure.
HTML:
<iframe id="AnalysisFrame" ng-src="{{kibanaUrl}}" ng-init="setupFrame()"></iframe>
JavaScript:
$scope.setupFrame = function() {
//iframes are excluded from mutation observation, so we will
// need to create an observer _inside_ the iframe content.
var theFrame = document.querySelector('#AnalysisFrame');
//once the frame is loaded, that is when we can now attach our
// observer to the frame's content.
theFrame.onload = function() {
//console.log('[TRACE] iframe is completely loaded');
var bIsKibanaAdmin = $scope.bIsKibanaAdmin;
//the above is TRUE|FALSE set by some outside logic
// which does not pertain to this exercise.
//create an observer instance for Management sub-menus
var observerMan = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
//console.log('[TRACE] ', mutation);
//sub-menus of the Management console area
var manArea = theFrame.contentDocument.body.querySelector('kbn-management-landing');
if ( manArea ) {
//Management area is divided up by panels of related subjects
var manPanels = manArea.querySelectorAll('div[class*="page-row"]');
if ( manPanels ) manPanels.forEach(function(aPanel) {
//console.log('[TRACE] panel=', aPanel);
//6.2.4 had <div> titles, 6.3.x has <h3> titles, select based on their class only
var panelTitle = aPanel.querySelector('.kuiPanelHeader__title');
//if a panel has a title (version panel does not have one), see if hide or not.
if ( panelTitle ) switch ( panelTitle.innerText ) {
case 'Kibana':
//only hide the Advanced Settings item off this panel
var panelItem = aPanel.querySelector('li > a[href*="#/management/kibana/settings"]');
if ( panelItem ) panelItem.parentElement.hidden = !bIsKibanaAdmin;
break;
default:
//most management panels are hidden from non-admins
aPanel.hidden = !bIsKibanaAdmin;
}//switch
});
}
});
});
//configuration of the left menu becomes active observer
var configMan = {
attributes: true, //for when Management becomes the Active menu
attributeFilter: ['class'],
//attributeOldValue: true,
childList: false,
characterData: false,
//characterDataOldValue: false,
//subtree: true,
};
//the Management menu item does not exist yet, cannot start observing until later.
//create an observer instance for left menu
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
//console.log('[TRACE] ', mutation);
var leftMenus = mutation.target.querySelectorAll('.global-nav-link');
//since the menus do not have IDs, we are searching for their outer
// DIV class unique to all left menu items. Their first element child
// will be the <a href> link we can use to filter out ones we
// wish to show or not.
if ( leftMenus ) leftMenus.forEach(function(aMenu) {
if ( aMenu.firstElementChild ) {
switch ( aMenu.firstElementChild.hash ) {
case "#/dev_tools":
aMenu.hidden = !bIsKibanaAdmin;
break;
case "#/account":
aMenu.hidden = true;
break;
case "":
if ( aMenu.innerText=='Logout' ) {
aMenu.hidden = true;
}
//else console.log('[TRACE] menu=', aMenu);
break;
case "#/management":
//we only want to hide certain sub-menus
// our menu item exists, attach observer for when
// user clicks it to make it "active"
observerMan.observe(aMenu, configMan);
break;
default:
//console.log('[TRACE] menu=', aMenu);
}//switch
}
});
});
});
//configuration of the left menu creation observer
var configLM = {
attributes: false,
//attributeFilter: ['src'],
//attributeOldValue: true,
childList: true,
characterData: false,
//characterDataOldValue: false,
//subtree: true,
};
//start observing the contents of the iframe changes
observer.observe(theFrame.contentDocument.body, configLM);
};
};

open "view details" page in new tab at alfresco workflow form

open "view details" page in new tab at alfresco workflow accept/reject page but ** also to retain focus on the current page (approval/rejection) page**
with target=_blank focus on current page is getting lost
****overriding object-finder.js here****
function ObjectFinder_fnRenderCellListItemName(elCell, oRecord, oColumn, oData)
{
if (scope.options.showLinkToTarget && scope.options.targetLinkTemplate !== null)
{
if(item.site=="######xxxxxxxxxxany site****"){
title = '**' + $html(item.displayName?item.displayName:item.name) + '';
}
}
ObjectFinder_fnRenderCellListItemActions(elCell, oRecord, oColumn, oData)
{
if (scope.options.disabled === false)
{
var links = "", link, listAction;
for (var i = 0, il = scope.options.listItemActions.length; i < il; i++)
{
listAction = scope.options.listItemActions[i];
if (listAction.event)
{
if(item.site=="xx Any RAndom Site xxxxx"){
links += '<div class="list-action">'** + scope.msg(listAction.label) + '</div>';
}
Staying in the current tab when opening a new tab is controlled by the browser and there are some workarounds are present here.
You may need to simulate "Ctrl" + Click event while clicking the link.Please refer the below links.
https://stackoverflow.com/a/36027776/405317
Open a new tab in the background?
https://www.quora.com/How-do-I-open-a-link-in-a-new-tab-but-stay-on-the-same-page-using-HTML-code
https://productforums.google.com/forum/#!topic/chrome/RCCFMX3b7NA
Hope you will get some idea.

how to add a "next fieldset" button to an add/edit form for a Dexterity Plone type

I have a Dexterity type that has multiple fieldsets, and the built-in Javascript that allows showing one fieldset at a time when adding or editing is wonderful.
But I'd like to invite the user to walk through the fieldsets in sequence, so my ideal situation would not present the "Submit" button until the last fieldset was visible, instead presenting NEXT> or <PREV and NEXT> buttons until that last fieldset.
I gather that this is a behavior? But I'm at a bit of a loss as to how to add it and how to control it. I'm currently using the default EditForm, and I'd much prefer to just make a tiny tweak, but if it means dropping down to building the form myself, that's OK. I just need to know whether that's the only way to get this addition, which seems unlikely.
The fieldsets can be rigged up to add 'previous' and 'next' buttons with some extra JavaScript magic. Here's what I use in a current project:
var prevnext = {
formTabs: null,
next: function() { prevnext.formTabs.data('tabs').next(); prevnext._scrollToTop(); },
prev: function() { prevnext.formTabs.data('tabs').prev(); prevnext._scrollToTop(); },
_scrollToTop: function() {
$(window).scrollTop(prevnext.formTabs.closest('form').offset().top);
},
showButtons: function(event, index) {
var tabs = prevnext.formTabs.data('tabs'),
index = typeof(index) === 'undefined' ? tabs.getIndex() : index,
current = tabs.getTabs()[index],
count = tabs.getTabs().length;
$('#prevnext_previous').toggle(index !== 0);
$('#prevnext_next').toggle(index !== (count - 1));
$('.formControls:last :submit[name=form_submit]').toggle(index === )count - 1));
},
init: function() {
var tabs;
prevnext.formTabs = $('.formTabs');
tabs = prevnext.formTabs.data('tabs');
if (tabs.getTabs().length > 0) {
if ($('fieldset#fieldset-distribution').length === 0)
return;
$('.formControls:last :submit:first')
.before($('<input id="prevnext_previous" class="context" ' +
' type="button" value="" />')
.val('< Previous')
.click(prevnext.prev))
.before(document.createTextNode(' '));
$('.formControls:last :submit:first')
.before($('<input id="prevnext_next" class="context" ' +
' type="button" value="" />')
.val('Next >')
.click(prevnext.next))
.before(document.createTextNode(' '));
prevnext.showButtons();
tabs.onClick(prevnext.showButtons);
}
}
};
$(prevnext.init());

Resources