Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit da6baac

Browse files
crisbetokara
authored andcommitted
fix(docs): prevent tabbing over hidden content; better animation handling (#9773)
* Fixes users being able to tab through the hidden content on the docs site. * Switches the docs site's menu accordions to use `$animateCss` for their transitions. This allows us to reliably know when an animation has started and finished. This approach also avoids having to trigger multiple nested timeouts. Fixes #8896.
1 parent 14ab34c commit da6baac

File tree

3 files changed

+47
-47
lines changed

3 files changed

+47
-47
lines changed

docs/app/css/style.css

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,8 +355,7 @@ body[dir=rtl] .menu-heading {
355355
overflow: hidden;
356356
position: relative;
357357
z-index: 1;
358-
transition: 0.75s cubic-bezier(0.35, 0, 0.25, 1);
359-
transition-property: height;
358+
height: 0;
360359
}
361360
.docs-menu .menu-toggle-list a.md-button {
362361
display: block;

docs/app/js/app.js

Lines changed: 41 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ function(SERVICES, COMPONENTS, DEMOS, PAGES, $location, $rootScope, $http, $wind
492492
};
493493
})
494494

495-
.directive('menuToggle', [ '$timeout', '$mdUtil', function($timeout, $mdUtil) {
495+
.directive('menuToggle', ['$mdUtil', '$animateCss', '$$rAF', function($mdUtil, $animateCss, $$rAF) {
496496
return {
497497
scope: {
498498
section: '='
@@ -501,59 +501,56 @@ function(SERVICES, COMPONENTS, DEMOS, PAGES, $location, $rootScope, $http, $wind
501501
link: function($scope, $element) {
502502
var controller = $element.parent().controller();
503503

504+
// Used for toggling the visibility of the accordion's content, after
505+
// all of the animations are completed. This prevents users from being
506+
// allowed to tab through to the hidden content.
507+
$scope.renderContent = false;
508+
504509
$scope.isOpen = function() {
505510
return controller.isOpen($scope.section);
506511
};
512+
507513
$scope.toggle = function() {
508514
controller.toggleOpen($scope.section);
509515
};
510516

511517
$mdUtil.nextTick(function() {
512-
$scope.$watch(
513-
function () {
514-
return controller.isOpen($scope.section);
515-
},
516-
function (open) {
517-
// We must run this in a next tick so that the getTargetHeight function is correct
518-
$mdUtil.nextTick(function() {
519-
var $ul = $element.find('ul');
518+
$scope.$watch(function () {
519+
return controller.isOpen($scope.section);
520+
}, function (open) {
521+
var $ul = $element.find('ul');
522+
var $li = $ul[0].querySelector('a.active');
523+
524+
if (open) {
525+
$scope.renderContent = true;
526+
}
527+
528+
$$rAF(function() {
529+
var targetHeight = open ? $ul[0].scrollHeight : 0;
530+
531+
$animateCss($ul, {
532+
easing: 'cubic-bezier(0.35, 0, 0.25, 1)',
533+
to: { height: targetHeight + 'px' },
534+
duration: 0.75 // seconds
535+
}).start().then(function() {
520536
var $li = $ul[0].querySelector('a.active');
521-
var docsMenuContent = document.querySelector('.docs-menu').parentNode;
522-
var targetHeight = open ? getTargetHeight() : 0;
523-
524-
$timeout(function () {
525-
// Set the height of the list
526-
$ul.css({height: targetHeight + 'px'});
527-
528-
// If we are open and the user has not scrolled the content div; scroll the active
529-
// list item into view.
530-
if (open && $li && $li.offsetParent && $ul[0].scrollTop === 0) {
531-
$timeout(function() {
532-
var activeHeight = $li.scrollHeight;
533-
var activeOffset = $li.offsetTop;
534-
var parentOffset = $li.offsetParent.offsetTop;
535-
536-
// Reduce it a bit (2 list items' height worth) so it doesn't touch the nav
537-
var negativeOffset = activeHeight * 2;
538-
var newScrollTop = activeOffset + parentOffset - negativeOffset;
539-
540-
$mdUtil.animateScrollTo(docsMenuContent, newScrollTop);
541-
}, 350, false);
542-
}
543-
}, 0, false);
544-
545-
function getTargetHeight() {
546-
var targetHeight;
547-
$ul.addClass('no-transition');
548-
$ul.css('height', '');
549-
targetHeight = $ul.prop('clientHeight');
550-
$ul.css('height', 0);
551-
$ul.removeClass('no-transition');
552-
return targetHeight;
537+
538+
$scope.renderContent = open;
539+
540+
if (open && $li && $ul[0].scrollTop === 0) {
541+
var activeHeight = $li.scrollHeight;
542+
var activeOffset = $li.offsetTop;
543+
var parentOffset = $li.offsetParent.offsetTop;
544+
545+
// Reduce it a bit (2 list items' height worth) so it doesn't touch the nav
546+
var negativeOffset = activeHeight * 2;
547+
var newScrollTop = activeOffset + parentOffset - negativeOffset;
548+
549+
$mdUtil.animateScrollTo(document.querySelector('.docs-menu').parentNode, newScrollTop);
553550
}
554-
}, false);
555-
}
556-
);
551+
});
552+
});
553+
});
557554
});
558555

559556
var parentNode = $element[0].parentNode.parentNode.parentNode;

docs/app/partials/menu-toggle.tmpl.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@
1515
</span>
1616
</md-button>
1717

18-
<ul id="docs-menu-{{section.name | nospace}}" class="menu-toggle-list">
18+
<ul id="docs-menu-{{section.name | nospace}}"
19+
class="menu-toggle-list"
20+
aria-hidden="{{!renderContent}}"
21+
ng-style="{ visibility: renderContent ? 'visible' : 'hidden' }">
22+
1923
<li ng-repeat="page in section.pages">
2024
<menu-link section="page"></menu-link>
2125
</li>

0 commit comments

Comments
 (0)