-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbooks_read.html
166 lines (147 loc) · 6.82 KB
/
books_read.html
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Searchable book list</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Here's where I keep a list of books I have read.</h1>
This list was curated by <a href="index.html">Lexington Whalen</a>, beginning from his first year of PhD to end. As he is me, I hope he keeps going!
I typically use this to organize books I found interesting. Please feel free to do whatever you want with it.
<p id="bookCount">So far, we have read 0 books.</p>
<small id = "searchCount">
Your search returned 0 books. Nice!
</small>
<div class="search-inputs">
<input type="text" id="titleSearch" placeholder="Search title...">
<input type="text" id="authorSearch" placeholder="Search author...">
<input type="text" id="dateCompleteSearch" placeholder="Search date complete...">
<input type="text" id="pageCountSearch" placeholder="Search page count...">
<input type="text" id="ratingSearch" placeholder="Search rating...">
<input type="text" id="descriptionSearch" placeholder="Search description...">
<button id="clearSearch">Clear Search</button>
</div>
<table id="bookTable">
<thead>
<tr>
<th data-sort="title">Title</th>
<th data-sort="author">Author</th>
<th data-sort="date-complete">Date Complete</th>
<th data-sort="page-count">Page Count</th>
<th data-sort="rating">Rating</th>
<th data-sort="description">Description</th>
</tr>
</thead>
<tbody>
<!-- Table body will be populated by JavaScript -->
</tbody>
</table>
<script>
const table = document.getElementById('bookTable');
const tbody = table.querySelector('tbody');
const clearButton = document.getElementById('clearSearch');
const searchInputs = {
title: document.getElementById('titleSearch'),
author: document.getElementById('authorSearch'),
'date-complete': document.getElementById('dateCompleteSearch'),
'page-count': document.getElementById('pageCountSearch'),
rating: document.getElementById('ratingSearch'),
description: document.getElementById('descriptionSearch')
};
const bookCountElement = document.getElementById('bookCount');
let sortOrder = {};
// Load books from JSON file
fetch('books/list.json')
.then(response => response.json())
.then(books => {
populateTable(books);
updateBookCount(books.length);
setupEventListeners();
})
.catch(error => console.error('Error loading books:', error));
function populateTable(books) {
books.forEach(book => {
const row = document.createElement('tr');
for (let key in searchInputs) {
const cell = document.createElement('td');
cell.textContent = book[key];
row.appendChild(cell);
}
tbody.appendChild(row);
});
}
function updateBookCount(count) {
bookCountElement.textContent = `So far, we have read ${count} books. Let's keep it up!`;
searchCountElement.textContent = `Your search returned ${count} books. Nice!`
}
function setupEventListeners() {
for (let key in searchInputs) {
searchInputs[key].addEventListener('keyup', searchTable);
}
clearButton.addEventListener('click', function() {
for (let key in searchInputs) {
searchInputs[key].value = '';
}
searchTable();
});
table.querySelector('thead').addEventListener('click', function(e) {
const th = e.target.closest('th');
if (!th) return;
const column = th.dataset.sort;
const dataType = (column === 'page-count') ? 'number' : 'string';
sortOrder[column] = sortOrder[column] === 'asc' ? 'desc' : 'asc';
sortTable(column, dataType, sortOrder[column]);
});
}
function searchTable() {
const rows = tbody.getElementsByTagName('tr');
let numRowsMatch = 0;
for (let i = 0; i < rows.length; i++) {
const row = rows[i];
const cells = row.getElementsByTagName('td');
let foundMatch = true;
for (let key in searchInputs) {
const cellText = cells[Object.keys(searchInputs).indexOf(key)].textContent.toLowerCase();
const searchTerm = searchInputs[key].value.toLowerCase();
if (searchTerm && !cellText.includes(searchTerm)) {
foundMatch = false;
break;
}
}
if(foundMatch){
row.style.display = '';
numRowsMatch++;
}else{
row.style.display='none';
}
}
updateSearchCount(numRowsMatch);
}
function sortTable(column, dataType, order) {
const rows = Array.from(tbody.querySelectorAll('tr'));
const columnIndex = Array.from(table.querySelector('thead tr').children).findIndex(th => th.dataset.sort === column);
rows.sort((a, b) => {
let aValue = a.children[columnIndex].textContent;
let bValue = b.children[columnIndex].textContent;
if (dataType === 'number') {
return order === 'asc' ? Number(aValue) - Number(bValue) : Number(bValue) - Number(aValue);
} else if (column === 'date-complete') {
return order === 'asc' ? new Date(aValue) - new Date(bValue) : new Date(bValue) - new Date(aValue);
} else if (column === 'rating') {
return order === 'asc' ? Number(aValue.split('/')[0]) - Number(bValue.split('/')[0]) : Number(bValue.split('/')[0]) - Number(aValue.split('/')[0]);
} else {
return order === 'asc' ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
}
});
tbody.append(...rows);
}
// for getting number of found results
const searchCountElement = document.getElementById('searchCount');
function updateSearchCount(count) {
searchCountElement.textContent = `Your search returned ${count} books. Nice!`;
}
</script>
</body>
</html>