mirror of https://github.com/istio/istio.io.git
Finish keyboard navigation for header & menu. (#3607)
This commit is contained in:
parent
3e6c061cd5
commit
74e7ef56b1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<main class="notfound" role="main">
|
||||
<svg class="icon">
|
||||
<use xlink:href="{{ .Site.BaseURL }}/img/icons.svg#exclamation-mark"/>
|
||||
<use xlink:href="{{ .Site.BaseURL }}img/icons.svg#exclamation-mark"/>
|
||||
</svg>
|
||||
|
||||
<div class="error">
|
||||
|
|
|
|||
|
|
@ -141,40 +141,38 @@
|
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" defer></script>
|
||||
{{ end }}
|
||||
|
||||
<nav>
|
||||
<div id="scroll-to-top-container">
|
||||
<button id="scroll-to-top" aria-hidden="true" title='{{ i18n "button_top"}}'>
|
||||
{{ partial "icon.html" "top" }}
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
<div id="scroll-to-top-container" aria-hidden="true">
|
||||
<button id="scroll-to-top" title='{{ i18n "button_top"}}'>
|
||||
{{ partial "icon.html" "top" }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{ if .Site.Data.args.preliminary }}
|
||||
<div id="switch-lang-container">
|
||||
<div id="switch-lang-container" aria-hidden="true">
|
||||
{{ $home := .Site.GetPage "home" }}
|
||||
<a id="switch-lang" data-skipendnotes="true" aria-hidden="true" title='{{ i18n "switch_lang"}}'>
|
||||
<a id="switch-lang" data-skipendnotes="true" title='{{ i18n "switch_lang"}}'>
|
||||
{{ partial "icon.html" "switch-lang" }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{ if .Page.Params.source_repo }}
|
||||
<div id="edit-this-page-container">
|
||||
<div id="edit-this-page-container" aria-hidden="true">
|
||||
{{ $msg := i18n "generated_file" }}
|
||||
{{ $title := printf $msg .Page.Params.source_repo }}
|
||||
|
||||
<a tabindex="-1" id="edit-this-page" class="disabled" aria-hidden="true" title='{{ $title }}'>
|
||||
<a tabindex="-1" id="edit-this-page" class="disabled" title='{{ $title }}'>
|
||||
{{ partial "icon.html" "pencil" }}
|
||||
</a>
|
||||
</div>
|
||||
{{ else }}
|
||||
<div id="edit-this-page-container">
|
||||
<a tabindex="-1" id="edit-this-page" data-skipendnotes="true" href="https://github.com/istio/istio.io/edit/{{ .Site.Data.args.doc_branch_name }}/content/{{ .Path }}" aria-hidden="true" title='{{ i18n "edit_on_github"}}'>
|
||||
<div id="edit-this-page-container" aria-hidden="true">
|
||||
<a tabindex="-1" id="edit-this-page" data-skipendnotes="true" href="https://github.com/istio/istio.io/edit/{{ .Site.Data.args.doc_branch_name }}/content/{{ .Path }}" title='{{ i18n "edit_on_github"}}'>
|
||||
{{ partial "icon.html" "pencil" }}
|
||||
</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<div id="report-site-bugs-container">
|
||||
<div id="report-site-bugs-container" aria-hidden="true">
|
||||
<a tabindex="-1" id="report-site-bugs" data-skipendnotes="true" href="https://github.com/istio/istio.io/issues/new?title=Issue%20with%20{{ .Path}}" aria-hidden="true" title='{{ i18n "report_site_bugs"}}'>
|
||||
{{ partial "icon.html" "bugs" }}
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -12,14 +12,16 @@
|
|||
{{ .LinkTitle }}
|
||||
</div>
|
||||
|
||||
<div class="body">
|
||||
{{ $questions := .Resources.ByType "page" }}
|
||||
{{ $sorted_questions := sort $questions ".Params.weight" }}
|
||||
{{ $url := .Permalink }}
|
||||
{{ range $q := $sorted_questions }}
|
||||
<a href="{{ $url }}#{{ $q.File.BaseFileName | urlize }}">{{ $q.Title }}</a><br/>
|
||||
{{ end }}
|
||||
</div>
|
||||
<nav class="body">
|
||||
<ul aria-label="{{ .LinkTitle }}">
|
||||
{{ $questions := .Resources.ByType "page" }}
|
||||
{{ $sorted_questions := sort $questions ".Params.weight" }}
|
||||
{{ $url := .Permalink }}
|
||||
{{ range $q := $sorted_questions }}
|
||||
<li role="none"><a href="{{ $url }}#{{ $q.File.BaseFileName | urlize }}">{{ $q.Title }}</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
|
|
|||
|
|
@ -53,38 +53,39 @@
|
|||
{{ end }}
|
||||
|
||||
<div class="menu">
|
||||
<button id="gearDropdown" class="menu-trigger" title='{{ i18n "options_menu" }}' aria-label="Options and Settings" aria-haspopup="true" aria-expanded="false">
|
||||
<button id="gearDropdownButton" class="menu-trigger" title='{{ i18n "options_menu" }}'
|
||||
aria-label="Options and Settings" aria-controls="gearDropdownContent">
|
||||
{{ partial "icon.html" "gear" }}
|
||||
</button>
|
||||
|
||||
<div class="menu-content" aria-labelledby="gearDropdown">
|
||||
<a tabindex="0" lang="en" id="switch-lang-en" {{ if eq $home.Lang "en" }}class="active"{{ end }}>English</a>
|
||||
<a tabindex="0" lang="zh" id="switch-lang-zh" {{ if eq $home.Lang "zh" }}class="active"{{ end }}>中文</a>
|
||||
<div id="gearDropdownContent" class="menu-content" aria-labelledby="gearDropdownButton" role="menu">
|
||||
<a tabindex="-1" role="menuitem" lang="en" id="switch-lang-en" {{ if eq $home.Lang "en" }}class="active"{{ end }}>English</a>
|
||||
<a tabindex="-1" role="menuitem" lang="zh" id="switch-lang-zh" {{ if eq $home.Lang "zh" }}class="active"{{ end }}>中文</a>
|
||||
|
||||
<div></div>
|
||||
<div role="separator"></div>
|
||||
|
||||
<a tabindex="0" class="active" id="light-theme-item">{{ i18n "light_theme" }}</a>
|
||||
<a tabindex="0" id="dark-theme-item">{{ i18n "dark_theme" }}</a>
|
||||
<a tabindex="-1" role="menuitem" class="active" id="light-theme-item">{{ i18n "light_theme" }}</a>
|
||||
<a tabindex="-1" role="menuitem" id="dark-theme-item">{{ i18n "dark_theme" }}</a>
|
||||
|
||||
<div></div>
|
||||
<div role="separator"></div>
|
||||
|
||||
<a tabindex="0" id="syntax-coloring-item">{{ i18n "syntax_coloring" }}</a>
|
||||
<a tabindex="-1" role="menuitem" id="syntax-coloring-item">{{ i18n "syntax_coloring" }}</a>
|
||||
|
||||
{{ if not .Site.Data.args.archive }}
|
||||
<div></div>
|
||||
<div role="separator"></div>
|
||||
|
||||
{{ if .Params.source_repo }}
|
||||
{{ $msg := i18n "generated_file" }}
|
||||
{{ $title := printf $msg .Page.Params.source_repo }}
|
||||
<a class="disabled" title="{{ $title }}">{{ i18n "edit_on_github" }}</a>
|
||||
<a tabindex="-1" role="menuitem" class="disabled" title="{{ $title }}">{{ i18n "edit_on_github" }}</a>
|
||||
{{ else }}
|
||||
<a href="https://github.com/istio/istio.io/edit/{{ .Site.Data.args.doc_branch_name }}/content/{{ .Path }}">{{ i18n "edit_on_github" }}</a>
|
||||
<a tabindex="-1" role="menuitem" href="https://github.com/istio/istio.io/edit/{{ .Site.Data.args.doc_branch_name }}/content/{{ .Path }}">{{ i18n "edit_on_github" }}</a>
|
||||
{{ end }}
|
||||
|
||||
<a href="https://github.com/istio/istio.io/issues/new?title=Issue%20with%20{{ .Path}}">{{ i18n "report_site_bugs" }}</a>
|
||||
<a tabindex="-1" role="menuitem" href="https://github.com/istio/istio.io/issues/new?title=Issue%20with%20{{ .Path}}">{{ i18n "report_site_bugs" }}</a>
|
||||
{{ end }}
|
||||
|
||||
<div></div>
|
||||
<div role="separator"></div>
|
||||
|
||||
<h6>{{ i18n "other_versions_of_site" }}</h6>
|
||||
|
||||
|
|
@ -92,20 +93,20 @@
|
|||
{{ $current := index .Site.Data.releases 1 }}
|
||||
|
||||
{{ if .Site.Data.args.archive }}
|
||||
<a tabindex="0" onclick="navigateToUrlOrRoot('https://istio.io/{{.Dir}}');return false;">{{ printf (i18n "current_release") $current.name }}</a>
|
||||
<a tabindex="0" onclick="navigateToUrlOrRoot('https://preliminary.istio.io/{{.Dir}}');return false;">{{ printf (i18n "next_release") $next.name }}</a>
|
||||
<a href="https://archive.istio.io">{{ i18n "archived_releases" }}</a>
|
||||
<a tabindex="-1" role="menuitem" onclick="navigateToUrlOrRoot('https://istio.io/{{.Dir}}');return false;">{{ printf (i18n "current_release") $current.name }}</a>
|
||||
<a tabindex="-1" role="menuitem" onclick="navigateToUrlOrRoot('https://preliminary.istio.io/{{.Dir}}');return false;">{{ printf (i18n "next_release") $next.name }}</a>
|
||||
<a tabidnex="-1" role="menuitem" href="https://archive.istio.io">{{ i18n "archived_releases" }}</a>
|
||||
{{ else if .Site.Data.args.preliminary }}
|
||||
<a tabindex="0" onclick="navigateToUrlOrRoot('https://istio.io/{{.Dir}}');return false;">{{ printf (i18n "current_release") $current.name }}</a>
|
||||
<a href="https://archive.istio.io">{{ i18n "archived_releases" }}</a>
|
||||
<a tabindex="-1" role="menuitem" onclick="navigateToUrlOrRoot('https://istio.io/{{.Dir}}');return false;">{{ printf (i18n "current_release") $current.name }}</a>
|
||||
<a tabindex="-1" role="menuitem" href="https://archive.istio.io">{{ i18n "archived_releases" }}</a>
|
||||
{{ else }}
|
||||
<a tabindex="0" onclick="navigateToUrlOrRoot('https://preliminary.istio.io/{{.Dir}}');return false;">{{ printf (i18n "next_release") $next.name }}</a>
|
||||
<a href="https://archive.istio.io">{{ i18n "archived_releases" }}</a>
|
||||
<a tabindex="-1" role="menuitem" onclick="navigateToUrlOrRoot('https://preliminary.istio.io/{{.Dir}}');return false;">{{ printf (i18n "next_release") $next.name }}</a>
|
||||
<a tabindex="-1" role="menuitem" href="https://archive.istio.io">{{ i18n "archived_releases" }}</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button tabindex="0" id="search-show" title='{{ i18n "search" }}' aria-label='{{ i18n "search_label" }}'>{{ partial "icon.html" "magnifier" }}</button>
|
||||
<button id="search-show" title='{{ i18n "search" }}' aria-label='{{ i18n "search_label" }}'>{{ partial "icon.html" "magnifier" }}</button>
|
||||
</div>
|
||||
|
||||
<form id="search-form" name="cse" role="search">
|
||||
|
|
@ -118,7 +119,7 @@
|
|||
{{ end }}
|
||||
<input type="hidden" name="ie" value="utf-8" />
|
||||
<input type="hidden" name="hl" value="en" />
|
||||
<input type="hidden" id="search-page-url" value="{{ .Site.BaseURL }}/search.html" />
|
||||
<input type="hidden" id="search-page-url" value="{{ .Site.BaseURL }}search.html" />
|
||||
<input id="search-textbox" class="form-control" name="q" type="search" aria-label='{{ i18n "search" }}'/>
|
||||
<button id="search-close" title='{{ i18n "search_cancel" }}' type="reset" aria-label='{{ i18n "search_cancel" }}'>{{ partial "icon.html" "cancel-x" }}</button>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@
|
|||
|
||||
{{ if and $needTOC (not .Params.force_inline_toc) }}
|
||||
<div class="toc-container">
|
||||
<nav class="toc">
|
||||
<div id="toc" class="directory" role="directory">
|
||||
{{ $toc }}
|
||||
<nav class="toc" aria-label="Table of Contents">
|
||||
<div id="toc">
|
||||
{{ $toc | safeHTML }}
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -103,11 +103,11 @@
|
|||
</div>
|
||||
|
||||
{{ if $needTOC }}
|
||||
<div class="toc-inlined{{ if .Params.force_inline_toc }} toc-forced{{ end }}">
|
||||
<hr/>
|
||||
<div class="directory" role="directory">
|
||||
{{ replace $toc "TableOfContents" "InlinedTableOfContents" | safeHTML }}
|
||||
<nav class="toc-inlined{{ if .Params.force_inline_toc }} toc-forced{{ end }}" aria-label="Table of Contents">
|
||||
<div>
|
||||
<hr/>
|
||||
{{ $toc | safeHTML }}
|
||||
<hr/>
|
||||
</div>
|
||||
<hr/>
|
||||
</div>
|
||||
</nav>
|
||||
{{ end }}
|
||||
|
|
|
|||
|
|
@ -21,45 +21,43 @@
|
|||
|
||||
{{ if or (gt $len 0) ($page.Scratch.Get "seeAlso") }}
|
||||
{{ $page.Scratch.Set "needTOC" true }}
|
||||
<nav id="TableOfContents" aria-label="Table of Contents">
|
||||
<ul role="tree">
|
||||
{{ $page.Scratch.Set "level" 50 }}
|
||||
{{ range $h := $headers }}
|
||||
{{ $level := index (index (findRE "<h[23456].*?" $h) 0) 2 | int }}
|
||||
{{ $id := replaceRE "<h[23456].*?id=\"(.*?)\".*?>.*?</h[23456]>" "$1" $h }}
|
||||
{{ $title := replaceRE "<h[23456].*?>(.*?)</h[23456]>" "$1" $h }}
|
||||
{{ $current := $page.Scratch.Get "level" | int }}
|
||||
<ol>
|
||||
{{ $page.Scratch.Set "level" 50 }}
|
||||
{{ range $h := $headers }}
|
||||
{{ $level := index (index (findRE "<h[23456].*?" $h) 0) 2 | int }}
|
||||
{{ $id := replaceRE "<h[23456].*?id=\"(.*?)\".*?>.*?</h[23456]>" "$1" $h }}
|
||||
{{ $title := replaceRE "<h[23456].*?>(.*?)</h[23456]>" "$1" $h }}
|
||||
{{ $current := $page.Scratch.Get "level" | int }}
|
||||
|
||||
{{ if gt $level $current }}
|
||||
{{ $delta := sub $level $current }}
|
||||
{{ range $index, $num := (seq $delta) }}
|
||||
<ul role="group">
|
||||
{{ end }}
|
||||
{{ else if lt $level $current }}
|
||||
{{ $delta := sub $current $level }}
|
||||
{{ range $index, $num := (seq $delta) }}
|
||||
</ul>
|
||||
</li>
|
||||
{{ end }}
|
||||
{{ if gt $level $current }}
|
||||
{{ $delta := sub $level $current }}
|
||||
{{ range $index, $num := (seq $delta) }}
|
||||
<ol>
|
||||
{{ end }}
|
||||
|
||||
<li role="none" aria-label="{{ $title | safeHTML }}"><a role="treeitem" href="#{{ $id }}">{{ $title | safeHTML }}</a>
|
||||
|
||||
{{ $page.Scratch.Set "level" $level }}
|
||||
{{ end }}
|
||||
|
||||
{{ $delta := sub ($page.Scratch.Get "level") 50 }}
|
||||
{{ range $index, $num := (seq $delta) }}
|
||||
</ul>
|
||||
</li>
|
||||
{{ end }}
|
||||
|
||||
{{ if $page.Scratch.Get "seeAlso" }}
|
||||
{{ with $related }}
|
||||
<li role="none"><a role="treeitem" href="#see-also">{{ i18n "see_also" }}</a></li>
|
||||
{{ else if lt $level $current }}
|
||||
{{ $delta := sub $current $level }}
|
||||
{{ range $index, $num := (seq $delta) }}
|
||||
</ol>
|
||||
</li>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<li role="none" aria-label="{{ $title | safeHTML }}"><a href="#{{ $id }}">{{ $title | safeHTML }}</a>
|
||||
|
||||
{{ $page.Scratch.Set "level" $level }}
|
||||
{{ end }}
|
||||
|
||||
{{ $delta := sub ($page.Scratch.Get "level") 50 }}
|
||||
{{ range $index, $num := (seq $delta) }}
|
||||
</ol>
|
||||
</li>
|
||||
{{ end }}
|
||||
|
||||
{{ if $page.Scratch.Get "seeAlso" }}
|
||||
{{ with $related }}
|
||||
<li><a href="#see-also">{{ i18n "see_also" }}</a></li>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</ol>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ const mouseenter = 'mouseenter';
|
|||
const mouseleave = 'mouseleave';
|
||||
const active = 'active';
|
||||
const keyup = 'keyup';
|
||||
const keydown = 'keydown';
|
||||
const button = 'button';
|
||||
const ariaLabel = 'aria-label';
|
||||
const ariaExpanded = 'aria-expanded';
|
||||
181
src/js/menu.js
181
src/js/menu.js
|
|
@ -3,9 +3,186 @@
|
|||
// Attach the event handlers to support menus
|
||||
function handleMenu() {
|
||||
queryAll(document, '.menu').forEach(menu => {
|
||||
listen(query(menu, ".menu-trigger"), click, e => {
|
||||
e.cancelBubble = true;
|
||||
const trigger = query(menu, ".menu-trigger");
|
||||
const content = query(menu, ".menu-content");
|
||||
|
||||
// get all the menu items, setting role="menuitem" and tabindex="-1" along the way
|
||||
let items = [];
|
||||
for (let i = 0; i < content.children.length; i++) {
|
||||
const el = content.children[i];
|
||||
if (el.getAttribute("role") === 'menuitem') {
|
||||
items.push(el);
|
||||
}
|
||||
}
|
||||
|
||||
function focusTrigger() {
|
||||
trigger.focus();
|
||||
}
|
||||
|
||||
function focusFirstItem() {
|
||||
items[0].focus();
|
||||
}
|
||||
|
||||
function focusLastItem() {
|
||||
items[items.length - 1].focus();
|
||||
}
|
||||
|
||||
function focusNextItem(current) {
|
||||
const index = items.indexOf(current);
|
||||
if (index < items.length - 1) {
|
||||
items[index+1].focus();
|
||||
} else {
|
||||
items[0].focus();
|
||||
}
|
||||
}
|
||||
|
||||
function focusPrevItem(current) {
|
||||
const index = items.indexOf(current);
|
||||
if (index > 0) {
|
||||
items[index-1].focus();
|
||||
} else {
|
||||
items[items.length - 1].focus();
|
||||
}
|
||||
}
|
||||
|
||||
function getIndexFirstChars(startIndex, ch) {
|
||||
for (let i = startIndex; i < items.length; i++) {
|
||||
const firstChar = items[i].textContent.trim().substring(0, 1).toLowerCase();
|
||||
if (ch === firstChar) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function focusItemByChar(current, ch) {
|
||||
ch = ch.toLowerCase();
|
||||
|
||||
// Check remaining slots in the menu
|
||||
let index = getIndexFirstChars(items.indexOf(current) + 1, ch);
|
||||
|
||||
// If not found in remaining slots, check from beginning
|
||||
if (index === -1) {
|
||||
index = getIndexFirstChars(0, ch);
|
||||
}
|
||||
|
||||
// If match was found...
|
||||
if (index > -1) {
|
||||
if (!isActiveOverlay(menu)) {
|
||||
showOverlay(menu);
|
||||
toggleAttribute(trigger, ariaExpanded);
|
||||
}
|
||||
items[index].focus();
|
||||
}
|
||||
}
|
||||
|
||||
function isPrintableCharacter(str) {
|
||||
return str.length === 1 && str.match(/\S/);
|
||||
}
|
||||
|
||||
listen(trigger, click, e => {
|
||||
toggleOverlay(menu);
|
||||
toggleAttribute(e.currentTarget, ariaExpanded);
|
||||
e.cancelBubble = true;
|
||||
});
|
||||
|
||||
listen(trigger, keydown, e => {
|
||||
const ch = e.key;
|
||||
|
||||
if (e.ctrlKey || e.altKey || e.metaKey) {
|
||||
// nothing
|
||||
}
|
||||
else if (e.shiftKey) {
|
||||
if (isPrintableCharacter(ch)) {
|
||||
focusItemByChar(items[items.length - 1], ch);
|
||||
}
|
||||
} else {
|
||||
switch (e.keyCode) {
|
||||
case keyCodes.SPACE:
|
||||
case keyCodes.RETURN:
|
||||
case keyCodes.DOWN:
|
||||
showOverlay(menu);
|
||||
focusFirstItem();
|
||||
e.preventDefault();
|
||||
e.cancelBubble = true;
|
||||
break;
|
||||
|
||||
case keyCodes.UP:
|
||||
showOverlay(menu);
|
||||
focusLastItem();
|
||||
e.preventDefault();
|
||||
e.cancelBubble = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isPrintableCharacter(ch)) {
|
||||
focusItemByChar(items[items.length -1], ch);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
items.forEach(el => {
|
||||
listen(el, keydown, e => {
|
||||
const ch = e.key;
|
||||
|
||||
if (e.ctrlKey || e.altKey || e.metaKey) {
|
||||
// nothing
|
||||
}
|
||||
else if (e.shiftKey) {
|
||||
if (isPrintableCharacter(ch)) {
|
||||
focusItemByChar(el, ch);
|
||||
}
|
||||
} else {
|
||||
switch (e.keyCode) {
|
||||
case keyCodes.SPACE:
|
||||
break;
|
||||
|
||||
case keyCodes.RETURN:
|
||||
const evt = new MouseEvent("click", {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
clientX: 20,
|
||||
});
|
||||
el.dispatchEvent(evt);
|
||||
break;
|
||||
|
||||
case keyCodes.ESC:
|
||||
case keyCodes.TAB:
|
||||
focusTrigger();
|
||||
closeActiveOverlay();
|
||||
break;
|
||||
|
||||
case keyCodes.UP:
|
||||
focusPrevItem(el);
|
||||
break;
|
||||
|
||||
case keyCodes.DOWN:
|
||||
focusNextItem(el);
|
||||
break;
|
||||
|
||||
case keyCodes.HOME:
|
||||
case keyCodes.PAGEUP:
|
||||
focusFirstItem();
|
||||
break;
|
||||
|
||||
case keyCodes.END:
|
||||
case keyCodes.PAGEDOWN:
|
||||
focusLastItem();
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isPrintableCharacter(ch)) {
|
||||
focusItemByChar(el, ch);
|
||||
}
|
||||
break;
|
||||
}
|
||||
e.preventDefault();
|
||||
e.cancelBubble = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@
|
|||
let overlay = null;
|
||||
let popper = null;
|
||||
|
||||
function isActiveOverlay(element) {
|
||||
return overlay === element;
|
||||
}
|
||||
|
||||
// show/hide the specific overlay
|
||||
function toggleOverlay(element) {
|
||||
if (overlay === element) {
|
||||
|
|
@ -17,6 +21,16 @@ function toggleOverlay(element) {
|
|||
}
|
||||
}
|
||||
|
||||
// explicitly show the specific overlay
|
||||
function showOverlay(element) {
|
||||
if (overlay === element) {
|
||||
return;
|
||||
}
|
||||
closeActiveOverlay();
|
||||
element.classList.add('show');
|
||||
overlay = element;
|
||||
}
|
||||
|
||||
// explicitly close the active overlay
|
||||
function closeActiveOverlay() {
|
||||
if (overlay !== null) {
|
||||
|
|
|
|||
|
|
@ -13,11 +13,7 @@ function handleSidebar() {
|
|||
let button = e.currentTarget;
|
||||
button.classList.toggle("show");
|
||||
const ul = button.nextElementSibling.nextElementSibling;
|
||||
if (ul.getAttribute(ariaExpanded) === "true") {
|
||||
ul.setAttribute(ariaExpanded, "false");
|
||||
} else {
|
||||
ul.setAttribute(ariaExpanded, "true");
|
||||
}
|
||||
toggleAttribute(ul, ariaExpanded);
|
||||
|
||||
let el = ul;
|
||||
do {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,20 @@
|
|||
"use strict";
|
||||
|
||||
const keyCodes = Object.freeze({
|
||||
'TAB': 9,
|
||||
'RETURN': 13,
|
||||
'ESC': 27,
|
||||
'SPACE': 32,
|
||||
'PAGEUP': 33,
|
||||
'PAGEDOWN': 34,
|
||||
'END': 35,
|
||||
'HOME': 36,
|
||||
'LEFT': 37,
|
||||
'UP': 38,
|
||||
'RIGHT': 39,
|
||||
'DOWN': 40
|
||||
});
|
||||
|
||||
const escapeChars = {
|
||||
'¢': 'cent',
|
||||
'£': 'pound',
|
||||
|
|
@ -97,14 +112,22 @@ function getById(id) {
|
|||
return document.getElementById(id);
|
||||
}
|
||||
|
||||
function query(o, s) {
|
||||
return o.querySelector(s);
|
||||
function query(el, s) {
|
||||
return el.querySelector(s);
|
||||
}
|
||||
|
||||
function queryAll(o, s) {
|
||||
return o.querySelectorAll(s);
|
||||
function queryAll(el, s) {
|
||||
return el.querySelectorAll(s);
|
||||
}
|
||||
|
||||
function listen(o, e, f) {
|
||||
o.addEventListener(e, f);
|
||||
}
|
||||
|
||||
function toggleAttribute(el, name) {
|
||||
if (el.getAttribute(name) === "true") {
|
||||
el.setAttribute(name, "false");
|
||||
} else {
|
||||
el.setAttribute(name, "true");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,12 @@
|
|||
|
||||
.body {
|
||||
padding: 1.25rem;
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,6 +82,12 @@ header {
|
|||
margin-left: 4.2em;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
span {
|
||||
color: $textBrandHighlightColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a, button {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
.menu {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
.menu-trigger {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.menu-content {
|
||||
display: block;
|
||||
|
|
@ -16,6 +19,7 @@
|
|||
text-align: left;
|
||||
top: -600px;
|
||||
transition: top .5s;
|
||||
cursor: default;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
|
|
@ -24,7 +28,7 @@
|
|||
padding: .25rem 1.5rem;
|
||||
margin: 0;
|
||||
|
||||
&:hover {
|
||||
&:hover, &:focus {
|
||||
color: $textBrandColor;
|
||||
background-color: $mainBrandColor;
|
||||
text-decoration: none;
|
||||
|
|
@ -39,7 +43,7 @@
|
|||
background-size: .75rem .75rem;
|
||||
border: 0;
|
||||
|
||||
&:hover {
|
||||
&:hover, &:focus {
|
||||
background-image: $dropdownCheckHover;
|
||||
background-color: $mainBrandColor;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
top: $headerHeight;
|
||||
}
|
||||
|
||||
.directory {
|
||||
div {
|
||||
padding-left: .5em;
|
||||
border-left: 1px solid $dividerBarColor;
|
||||
|
||||
|
|
@ -42,13 +42,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
ol {
|
||||
list-style-type: none !important;
|
||||
padding-left: 0;
|
||||
padding-bottom: 0;
|
||||
margin: 0;
|
||||
|
||||
ul {
|
||||
ol {
|
||||
padding-left: 1em;
|
||||
}
|
||||
}
|
||||
|
|
@ -57,6 +57,7 @@
|
|||
|
||||
.toc-inlined {
|
||||
display: block;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
@media print {
|
||||
display: none;
|
||||
|
|
@ -66,20 +67,20 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
.directory {
|
||||
div {
|
||||
border-left: 0;
|
||||
|
||||
li {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
ul {
|
||||
ol {
|
||||
list-style-type: none !important;
|
||||
padding-left: 0;
|
||||
padding-bottom: 0;
|
||||
margin: 0;
|
||||
|
||||
ul {
|
||||
ol {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue