· 6 years ago · Feb 06, 2020, 09:46 AM
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>Document</title>
6 <style type="text/css">
7 html {
8 font-family: sans-serif;
9 }
10
11 .row {
12 display: flex;
13 }
14
15 .f8-link {
16 position: fixed;
17 bottom: 8px;
18 }
19
20 .search-bar {
21 position: relative;
22 }
23
24 .search-bar__history {
25 position: absolute;
26 top: 12px;
27 min-width: 200px;
28 padding-left: 0;
29 box-shadow: 0 0 3px #ccc;
30 display: none;
31 }
32
33 .search-bar__input:focus ~ .search-bar__history:not(:empty) {
34 display: block;
35 }
36
37 .search-bar__history-item {
38 list-style: none;
39 font-size: 14px;
40 padding: 4px 8px;
41 display: flex;
42 justify-content: space-between;
43 }
44
45 .search-bar__history-item:hover {
46 cursor: pointer;
47 background: #f7f7f7;
48 }
49
50 .search-bar__history-remove {
51 font-size: 12px;
52 color: #ED5D65;
53 }
54 </style>
55 <script src="${pageContext.request.contextPath}/js/jquery-3.2.0.min.js"></script>
56 <script src="${pageContext.request.contextPath}/js/pure.js"></script>
57 <script type="text/javascript">
58 /*
59 Khai báo các constants
60 */
61 const STORAGE_KEY = 'storageKey'; // key của data sẽ lưu trong localStorage
62 const KEYWORDS_KEY = 'searchKeywords'; // key của data lịch sử tìm kiếm
63 const ENTER_KEY_CODE = 13; // code của phím enter
64
65 //Khởi tạo đối tượng từ constructor function `Storage`
66 const Cache = new Storage(STORAGE_KEY);
67
68 //Lấy ra những nodes/elements trong DOM
69 const searchInputNode = document.querySelector('.search-bar__input'); // ô search input
70 const searchBtnNode = document.querySelector('.search-bar__search-btn'); // search button
71 const searchHistoryNode = document.querySelector('.search-bar__history'); // box lịch sử
72
73 //Khi click vào search button
74 searchBtnNode.onclick = function() {
75 handleSearch(searchInputNode.value);
76 };
77
78 //Khi click vào box lịch sử
79 searchHistoryNode.onclick = function(e) {
80 handleRemoveHistoryItem(e);
81 handleSearchByHistory(e);
82 };
83
84 //Khi nhấn phím `enter` trong ô input
85 searchInputNode.onkeydown = function(e) {
86 if (e.keyCode === ENTER_KEY_CODE) { // 13 là code của phím enter
87 handleSearch(searchInputNode.value);
88 }
89 }
90
91 /*
92 Khi click vào ô input, ta hiểu là đang `focus` tại ô input. Khi đó
93 box lịch sử tìm kiếm sẽ hiển thị (nếu có lịch sử)
94
95 Nếu ta `click` vào ô kết quả khi đó, box lịch sử sẽ bị ẩn vì hành động
96 mặc định khi click chuột ra khỏi input sẽ làm mất `focus` của input.
97
98 Mất `focus` của input box lịch sử sẽ ẩn đi và ta ko thể click vào lịch sử
99 tìm kiếm
100
101 Vì vậy ta cần bỏ qua hành vi mặc định này để có thể click vào box
102 lịch sử
103 */
104 searchHistoryNode.onmousedown = function(e) {
105 e.preventDefault(); // bỏ qua hành vi mặc định
106 }
107
108 //Render ra lịch sử tìm kiếm lần đầu khi trang web được load
109 renderSearchHistory();
110
111 //Hàm thực hiện tìm kiếm
112 function handleSearch(keyword) {
113 // Chỉ xử lý khi nhập từ khóa tìm kiếm
114 if (keyword) {
115 // Lấy lịch sử tìm kiếm ra và kiểm tra nếu chưa có
116 // từ khóa đang tìm trong lịch sử
117 // thì lưu từ khóa này vào lịch sử
118 const cachedKeywords = Cache.get(KEYWORDS_KEY, []);
119 if (!cachedKeywords.includes(keyword)) {
120 Cache.set(KEYWORDS_KEY, [...cachedKeywords, keyword]);
121
122 // Render lại lịch sử tìm kiếm
123 renderSearchHistory();
124 }
125
126 // Clear text tại ô tìm kiếm (nếu cần)
127 searchInputNode.value = '';
128
129 // Logic tìm kiếm bạn sẽ viết ở đây
130 alert(`Từ khóa: ${keyword}. Call API để tìm kiếm...`);
131 }
132 }
133
134 /*
135 Hàm xóa lịch sử tìm kiếm
136 */
137 function handleRemoveHistoryItem(event) {
138 if (event.target.classList.contains('search-bar__history-remove')) {
139 const historyIndex = event.target.getAttribute('data-index');
140 const cachedKeywords = Cache.get(KEYWORDS_KEY, []);
141 cachedKeywords.splice(historyIndex, 1);
142 Cache.set(KEYWORDS_KEY, cachedKeywords);
143 renderSearchHistory();
144 }
145 }
146
147 //Hàm xử lý tìm kiếm khi click vào từ khóa trong box lịch sử
148 function handleSearchByHistory(event) {
149 if (event.target.classList.contains('search-bar__history-item')) {
150 const historyValue = event.target.getAttribute('data-value');
151 handleSearch(historyValue);
152 }
153 }
154
155 //Hàm render ra list lịch sử tìm kiếm
156 function renderSearchHistory() {
157 let html = '';
158 // Lấy lịch sử và lặp qua, từ đó tạo ra HTML string
159 const histories = Cache.get(KEYWORDS_KEY, []);
160 let i = histories.length;
161 while(i > 0) {
162 i--;
163 html += `
164 <li class="search-bar__history-item" data-value="${histories[i]}">
165 ${histories[i]}
166 <span class="search-bar__history-remove" data-index="${i}">Remove</span>
167 </li>
168 `;
169 }
170
171 // Set HTML string vừa được tạo ra vào box lịch sử tìm kiếm
172 // trong VD cơ bản này tồn tại lỗi bảo mật XSS
173 // vì ví dụ này cơ bản, nên ta ko xử lý lỗi này. Các bạn hãy tìm hiểu thêm
174 searchHistoryNode.innerHTML = html;
175 }
176
177 /*
178 Hàm tạo đói tượng lưu trữ, ở đây ta đặt tên là `Storage`
179 Để hiểu về cách viết của hàm này, các bạn tìm hiểu:
180 1. Object constructor in Javascript
181 2. localStorage in Javascript
182 */
183 function Storage(storageKey) {
184 // Lấy data lưu tại `localStorage` ra, data lưu tại đây là JSON
185 // nên ta cần parse JSON string này để lấy được object và lưu vào biến `store`
186 const store = JSON.parse(localStorage.getItem(storageKey)) || {};
187
188 // Hàm lưu data của biến `store` vào localStorage, stringify để chuyển
189 // từ object thành JSON string
190 const save = function() {
191 localStorage.setItem(storageKey, JSON.stringify(store));
192 return store;
193 }
194
195 // Phương phức / hàm lấy giá trị được lưu trong `localStorage` theo `key`
196 // defaultValue sẽ được trả về nếu `key` chưa được lưu data
197 this.get = function(key, defaultValue = null) {
198 if (typeof store[key] !== 'undefined' && store[key]) {
199 return store[key];
200 }
201 return defaultValue;
202 }
203
204 // Phương thức / hàm set giá trị (lưu giá trị) vào `localStorage`
205 // chúng ta sẽ lưu theo `key`. Cách sử dụng: Cache.set('key_của_bạn', 'giá_trị_cần_lưu');
206 this.set = function(key, value) {
207 store[key] = value;
208 save();
209 return value;
210 }
211
212 // Bạn có thể add thêm các phương thức khác. VD. has, remove, flush, ...
213 }
214
215
216 </script>
217</head>
218<body>
219
220 <div class="row search-bar">
221 <input type="text" class="search-bar__input" placeholder="Enter something...Plz!">
222 <ul class="search-bar__history"></ul>
223 <button class="search-bar__search-btn">Search. Plz!</button>
224 </div>
225
226 <a class="f8-link" href="https://url.mycv.vn/?ref=jsfiddle&to=f8" target="_blank">Học lập trình miễn phí tại F8!</a>
227</body>
228</html>