forked from whatwg/html
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.js
134 lines (108 loc) · 3.16 KB
/
search.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
'use strict';
// Find-as-you-type search
(function() {
const search = document.getElementById('search');
const query = document.getElementById('query');
const resultsList = document.getElementById('results');
const maxResults = 10;
const UP_KEY_CODE = 38;
const DOWN_KEY_CODE = 40;
let jsonResponses = [];
fetch('search-index.json')
.then(response => response.json())
.then(data => jsonResponses = data)
.catch(err => console.error('Error loading search-index.json'));
function resultTemplate(result) {
return `<li><a href="${result.url}">${result.text} <span>Section ${result.section}</span></a></li>`;
}
function findSections(word, responses) {
// If input is empty, show nothing.
if (!word) {
return [];
}
return responses
.filter((response) => {
const regex = new RegExp(word, 'gi');
return response.text.match(regex);
})
.map(response => resultTemplate(response))
.slice(0, maxResults).join('');
}
function renderResults(results) {
resultsList.innerHTML = findSections(results, jsonResponses);
}
query.addEventListener('input', (event) => {
renderResults(event.target.value);
});
// Move between query and results with keyboard
document.addEventListener('keydown', (event) => {
const current = getCurrentNavigationElement();
if (!current) {
return;
}
const keyCode = event.keyCode;
let element;
if (keyCode === UP_KEY_CODE) {
element = getPreviousNavigationElement(current);
}
if (keyCode === DOWN_KEY_CODE) {
element = getNextNavigationElement(current);
}
if (element) {
element.focus();
event.preventDefault();
}
});
// Focus element on mouse over
resultsList.addEventListener('mousemove', (event) => {
const target = event.target;
if (target && target.nodeName === 'A') {
target.focus();
}
});
// If we go back from other place and click on the input, do the search again.
query.addEventListener('click', event => renderResults(query.value));
// When click on search area focus on input
search.addEventListener('click', event => query.focus());
// Slash to search '/'
document.addEventListener('keyup', (event) => {
if (event.keyCode === 191) {
query.focus();
}
});
// Clean results when click outside the search bar
document.addEventListener('click', (event) => {
if (event.target !== query) {
renderResults('');
}
});
function getCurrentNavigationElement() {
const active = document.activeElement;
if (active === query) {
return active;
}
const parent = active.parentNode;
if (parent) {
const grandparent = parent.parentNode;
if (grandparent === resultsList) {
return active;
}
}
return null;
}
function getNextNavigationElement(currentEl) {
if (currentEl === query) {
return resultsList.querySelector('a');
}
const nextLI = currentEl.parentNode.nextSibling;
return nextLI ? nextLI.firstChild : query;
}
function getPreviousNavigationElement(currentEl) {
const allResults = resultsList.querySelectorAll('a');
if (currentEl === query) {
return allResults.length > 0 ? allResults[allResults.length - 1] : null;
}
const prevLI = currentEl.parentNode.previousSibling;
return prevLI ? prevLI.firstChild : query;
}
})();