1 显示效果
可以看到在右上角有一个搜索图标,原本是这个主题带的,原本是点击以后会进入一个新的 search
页面进行搜索,但是我用的时候发现这个功能有问题,点击进去以后什么都没有出现,加上又会跳转到新页面,遂放弃,根据我的需求进行改进。
2 改进方向
总结起来我有几个想要的效果,便朝着这几个方向去改:
- 在同一个页面进行搜索,省去跳转的麻烦
- 搜索框和结果居中固定显示,并且有滚轮
- 为了可以和背景区分开,又不至于太僵硬,做成了毛玻璃效果
- 同步适应手机端的摆放位置优化
- 其他一些美化
那么就朝着这个方向开始做起来吧(并没有,其实都是做好以后再总结的😑
3 解决方案
2.1 添加搜索
在 layouts\partials\nav.html
中插入如下代码,在你想添加搜索的地方
1<li>
2 <a id="search-btn" href="javascript:void(0);">
3 <i class="fa fa-search"></i>
4 </a>
5</li>
6<script src="/js/fuse.min.js"></script>
7<script src="/js/fastsearch.js"></script>
8<link rel="stylesheet" type="text/css" href="/css/custom.css">
fuse.min.js
需要在github上下载一下。
2.2 添加 index.json
然后,添加 layouts\_default\index.json
文件:
1{{- $.Scratch.Add "index" slice -}}
2{{- range .Site.RegularPages -}}
3 {{- $.Scratch.Add "index" (dict "title" .Title "tags" .Params.tags "categories" .Params.categories "contents" .Plain "permalink" .Permalink "date" .Date "section" .Section) -}}
4{{- end -}}
5{{- $.Scratch.Get "index" | jsonify -}}
2.3 添加 css 文件
在 css\custom.css
中添加如下代码,
css和js文件都需要进行引用
1#fastSearch {
2 visibility: hidden;
3 position: absolute;
4 top: 200%;
5 left: 50%;
6 transform: translate(-50%, -50%);
7 display: inline-block;
8 width: 600px;
9 margin: 0 10px 0 0;
10 padding: 0;
11
12 }
13
14
15#fastSearch input {
16 /* padding: 4px; */
17 width: 100%;
18 height: 100%;
19 font-size: 1em;
20 color: #000000;
21 font-weight: bold;
22 /* background-color: #fffffffc; */
23 border-radius: 5px 5px 5px 5px;
24 border: none;
25 outline: none;
26 text-align: left;
27 display: inline-block;
28 padding: 10px;
29 backdrop-filter: blur(10px); /* 设置模糊效果,值越大模糊效果越明显 */
30 background-color: rgba(242, 242, 242, 0.472); /* 设置半透明的背景色 */
31}
32
33#searchResults li {
34 list-style: none;
35 margin-left: 0em;
36 /* background-color: #fffffffc; */
37 /* border-bottom: 1px dotted #000; */
38}
39
40#searchResults li .title {
41 font-size: 1em;
42 margin: 0;
43 color: #000;
44 display: inline-block;
45}
46
47
48
49#searchResults {
50 position: absolute; /* 使ul元素相对于#fastSearch进行定位 */
51 top: 100%; /* 调整ul元素的位置 */
52 left: 0; /* 调整ul元素的位置 */
53 width: 100%; /* 确保ul元素填充整个#fastSearch元素 */
54 list-style: none; /* 移除列表样式 */
55 padding: 0; /* 移除默认的ul元素内边距 */
56 border-radius: 5px 5px 5px 5px;
57 /* background-color: #fffffffc; */
58 max-height: 600px; /* 根据需要调整最大高度 */
59 overflow-y: auto; /* 当内容超过最大高度时显示滚动条 */
60 backdrop-filter: blur(10px); /* 设置模糊效果,值越大模糊效果越明显 */
61 background-color: rgba(242, 242, 242, 0.472); /* 设置半透明的背景色 */
62}
63
64
65#searchResults a {
66 text-decoration: none !important;
67 padding: 10px;
68 display: inline-block;
69 width: 100%;
70
71}
72
73#searchResults a:hover, #searchResults a:focus {
74 outline: 0;
75 background-color: rgba(243, 243, 243, 0.627);
76
77 color: #0085a1;
78}
79
80#searchResults li:hover a span {
81
82 color: #0085a1;
83 }
84
85
86#search-btn {
87 /* position: absolute;
88 top: 0px;
89 right: 0px; */
90 /* font-size: 24px; */
91}
92
93@media (max-width:683px) {
94 #search-btn {
95 top: 0px;
96 }
97 #fastSearch{
98 top: 100px;
99 width: 300px;
100 }
101
102}
103
104/* 对于WebKit浏览器(如Chrome和Safari) */
105::-webkit-input-placeholder {
106 color: rgba(73, 73, 73, 0.761);
107}
108
109/* 对于Mozilla Firefox 4至18 */
110:-moz-placeholder {
111 color: rgba(73, 73, 73, 0.761);
112 opacity: 1; /* Firefox默认给placeholder文字添加了透明度,所以需要设置opacity为1 */
113}
114
115/* 对于Mozilla Firefox 19+ */
116::-moz-placeholder {
117 color: rgba(73, 73, 73, 0.761);
118 opacity: 1;
119}
120
121/* 对于Internet Explorer 10-11 */
122:-ms-input-placeholder {
123 color: rgba(73, 73, 73, 0.761);
124}
125
126/* 对于Microsoft Edge */
127::-ms-input-placeholder {
128 color: rgba(73, 73, 73, 0.761);
129}
2.1 添加 js 文件
添加js文件 static\js\fastsearch.js
,并添加如下代码就大功告成了。
1var fuse; // holds our search engine
2var fuseIndex;
3var searchVisible = false;
4var firstRun = true; // allow us to delay loading json data unless search activated
5var list = document.getElementById('searchResults'); // targets the <ul>
6var first = list.firstChild; // first child of search list
7var last = list.lastChild; // last child of search list
8var maininput = document.getElementById('searchInput'); // input box for search
9var resultsAvailable = false; // Did we get any search results?
10
11// ==========================================
12// The main keyboard event listener running the show
13//
14document.addEventListener('keydown', function(event) {
15
16 // CMD-/ to show / hide Search
17 if (event.altKey && event.which === 191) {
18 // Load json search index if first time invoking search
19 // Means we don't load json unless searches are going to happen; keep user payload small unless needed
20 doSearch(event)
21 }
22
23 // Allow ESC (27) to close search box
24 if (event.keyCode == 27) {
25 if (searchVisible) {
26 document.getElementById("fastSearch").style.visibility = "hidden";
27 document.activeElement.blur();
28 searchVisible = false;
29 }
30 }
31
32 // DOWN (40) arrow
33 if (event.keyCode == 40) {
34 if (searchVisible && resultsAvailable) {
35 console.log("down");
36 event.preventDefault(); // stop window from scrolling
37 if ( document.activeElement == maininput) { first.focus(); } // if the currently focused element is the main input --> focus the first <li>
38 else if ( document.activeElement == last ) { last.focus(); } // if we're at the bottom, stay there
39 else { document.activeElement.parentElement.nextSibling.firstElementChild.focus(); } // otherwise select the next search result
40 }
41 }
42
43 // UP (38) arrow
44 if (event.keyCode == 38) {
45 if (searchVisible && resultsAvailable) {
46 event.preventDefault(); // stop window from scrolling
47 if ( document.activeElement == maininput) { maininput.focus(); } // If we're in the input box, do nothing
48 else if ( document.activeElement == first) { maininput.focus(); } // If we're at the first item, go to input box
49 else { document.activeElement.parentElement.previousSibling.firstElementChild.focus(); } // Otherwise, select the search result above the current active one
50 }
51 }
52});
53
54
55// ==========================================
56// execute search as each character is typed
57//
58document.getElementById("searchInput").onkeyup = function(e) {
59 executeSearch(this.value);
60}
61
62document.querySelector("body").onclick = function(e) {
63 if (e.target.tagName === 'BODY' || e.target.tagName === 'DIV') {
64 hideSearch()
65 }
66}
67
68document.querySelector("#search-btn").onclick = function(e) {
69 doSearch(e)
70}
71
72function doSearch(e) {
73 e.stopPropagation();
74 if (firstRun) {
75 loadSearch() // loads our json data and builds fuse.js search index
76 firstRun = false // let's never do this again
77 }
78 // Toggle visibility of search box
79 if (!searchVisible) {
80 showSearch() // search visible
81 }
82 else {
83 hideSearch()
84 }
85}
86
87function hideSearch() {
88 document.getElementById("fastSearch").style.visibility = "hidden" // hide search box
89 document.activeElement.blur() // remove focus from search box
90 searchVisible = false
91}
92
93function showSearch() {
94 document.getElementById("fastSearch").style.visibility = "visible" // show search box
95 document.getElementById("searchInput").focus() // put focus in input box so you can just start typing
96 searchVisible = true
97}
98
99// ==========================================
100// fetch some json without jquery
101//
102function fetchJSONFile(path, callback) {
103 var httpRequest = new XMLHttpRequest();
104 httpRequest.onreadystatechange = function() {
105 if (httpRequest.readyState === 4) {
106 if (httpRequest.status === 200) {
107 var data = JSON.parse(httpRequest.responseText);
108 if (callback) callback(data);
109 }
110 }
111 };
112 httpRequest.open('GET', path);
113 httpRequest.send();
114}
115
116
117// ==========================================
118// load our search index, only executed once
119// on first call of search box (CMD-/)
120//
121function loadSearch() {
122 console.log('loadSearch()')
123 fetchJSONFile('/index.json', function(data){
124
125 var options = { // fuse.js options; check fuse.js website for details
126 shouldSort: true,
127 location: 0,
128 distance: 100,
129 threshold: 0.4,
130 minMatchCharLength: 1,
131 keys: [
132 'permalink',
133 'title',
134 'tags',
135 'contents'
136 ]
137 };
138 // Create the Fuse index
139 fuseIndex = Fuse.createIndex(options.keys, data)
140 fuse = new Fuse(data, options, fuseIndex); // build the index from the json file
141 });
142}
143
144
145// ==========================================
146// using the index we loaded on CMD-/, run
147// a search query (for "term") every time a letter is typed
148// in the search box
149//
150function executeSearch(term) {
151 let results = fuse.search(term); // the actual query being run using fuse.js
152 let searchitems = ''; // our results bucket
153
154 if (results.length === 0) { // no results based on what was typed into the input box
155 resultsAvailable = false;
156 searchitems = '';
157 } else { // build our html
158 // console.log(results)
159 permalinks = [];
160 numLimit = 100;
161 for (let item in results) { // only show first 5 results
162 if (item > numLimit) {
163 break;
164 }
165 if (permalinks.includes(results[item].item.permalink)) {
166 continue;
167 }
168 // console.log('item: %d, title: %s', item, results[item].item.title)
169 searchitems = searchitems + '<li><a href="' + results[item].item.permalink + '" tabindex="0">' + '<span class="title">' + results[item].item.title + '</span></a></li>';
170 permalinks.push(results[item].item.permalink);
171 }
172 resultsAvailable = true;
173 }
174
175 document.getElementById("searchResults").innerHTML = searchitems;
176 if (results.length > 0) {
177 first = list.firstChild.firstElementChild; // first result container — used for checking against keyboard up/down location
178 last = list.lastChild.firstElementChild; // last result container — used for checking against keyboard up/down location
179 }
180}