早就想给博客加搜索功能了,但是之前忙着面试,现在面试凉了,有短暂的时间可以来做些自己的事情了,开整。

#搜索汇总

因为静态博客,所以需要把博客的全部内容打包成一个搜索集合,借助 hexo-generator-search,我们可以在生成静态文件的时候直接生成一个 search.xml,后续再通过 ajax 读取即可。

1
npm install hexo-generator-search --save

在博客根目录的 _config.yml 输入配置:

1
2
3
4
search:
path: search.xml
field: all
content: true

#搜索样式

接下来就要自己实现搜索了,Google 了一波,选了一个比较能契合主题的:https://codepen.io/choogoor/pen/NGJVMb

首先在 header.swig 里添加搜索所需要的 html 结构:

1
2
3
4
5
6
7
8
{%- if config.search -%}
<div class="search-button">
<a href="#" class="search-toggle" data-selector=".site-navbar"></a>
</div>
<div class="search-box">
<input type="text" id="local-search-input" class="text search-input" placeholder="Type here to search..." />
</div>
{%- endif -%}

然后添加搜索的 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
{%- if config.search -%}
<script id="search">
var searchFunc = function(path, search_id, content_id) {
'use strict';
$.ajax({
url: path,
dataType: "xml",
success: function( xmlResponse ) {
// get the contents from search data
var datas = $( "entry", xmlResponse ).map(function() {
return {
title: $( "title", this ).text(),
content: $("content",this).text(),
url: $( "url" , this).text()
};
}).get();

var $input = document.getElementById(search_id);
if (!$input) return;
var $resultContent = document.getElementById(content_id);

$input.addEventListener('input', function () {
var str = '<section class=\"posts\">';
var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
$resultContent.innerHTML = "";
if (this.value.trim().length <= 0) {
return;
}
// perform local searching
datas.forEach(function (data) {
var isMatch = true;
var content_index = [];
if (!data.title || data.title.trim() === '') {
data.title = "Untitled";
}
var data_title = data.title.trim().toLowerCase();
var data_content = data.content.trim().replace(/<[^>]+>/g, "").toLowerCase();
var data_url = data.url;
var index_title = -1;
var index_content = -1;
var first_occur = -1;
// only match artiles with not empty contents
if (data_content !== '') {
keywords.forEach(function (keyword, i) {
index_title = data_title.indexOf(keyword);
index_content = data_content.indexOf(keyword);

if (index_title < 0 && index_content < 0) {
isMatch = false;
} else {
if (index_content < 0) {
index_content = 0;
}
if (i == 0) {
first_occur = index_content;
}
// content_index.push({index_content:index_content, keyword_len:keyword_len});
}
});
} else {
isMatch = false;
}
// show search results
if (isMatch) {
str += `
<article class="post">
<header class="post-header">
<h1 class="post-title"><a class="post-link" href="`+ data_url +`">`+ data_title +`</a>
</h1>
</header>
<div class="post-content">
`;
var content = data.content.trim().replace(/<[^>]+>/g, "");
if (first_occur >= 0) {
// cut out 100 characters
var start = first_occur - 20;
var end = first_occur + 80;

if (start < 0) {
start = 0;
}

if (start == 0) {
end = 100;
}

if (end > content.length) {
end = content.length;
}

var match_content = content.substring(start, end);

// highlight all keywords
keywords.forEach(function (keyword) {
var regS = new RegExp(keyword, "gi");
match_content = match_content.replace(regS, "<code>" + keyword + "</code>");
});

str += "<p>" + match_content + "...</p>"
}
str += "</article>";
}
});
str += "</section>";
$resultContent.innerHTML = str;
});
}
});
}
var search_path = "{{ config.search.path }}";
if (search_path.length == 0) {
search_path = "search.xml";
}
var path = "{{ config.root }}" + search_path;
searchFunc(path, 'local-search-input', 'local-search-result');
</script>
{%- endif -%}

说起来简单,但是各种最后还是花了一下午的时间。

效果页面里就有咯。

#参考链接