搜索功能的实现最主要是生成一份关键字到站内链接的索引,当用户输入关键字后能够根据索引展示相应的链接。
通常,搜索引擎公司(例如Google,百度)会有自己的爬虫,当你将自己的网站地址提交给他们后,他们会用爬虫定期分析你的网站结构,然后生成索引文件并加入自己的索引库中。这样当终端用户输入和你的网站匹配的关键词后,你的网站内的链接就会在结果中显示出来。
站内搜索一般就不需要爬虫了,因为,其实最重要的是索引文件,只要你的网站更新的时候同时更新索引文件即可。

插入Google搜索框

你可以利用Hexo函数search_form直接插入Google搜索框。

search_form
插入 Google 搜索框。

1
<%- search_form(options) %>
参数 描述 默认值
class 表单的 class name search-form
text 搜索提示文字 Search
button 显示搜索按钮。此参数可为布尔值(boolean)或字符串,
当设定是字符串的时候,即为搜索按钮的文字。
false

自定义站内搜索

你也可以利用Hexo插件实现自己的站内搜索的功能。

安装插件

这个插件的本质是生成了一个搜索索引文件。
插件: hexo-generator-search
通过以下命令安装:

1
$ npm install hexo-generator-search --save

修改hexo配置

在_config.yml中添加站内搜索配置项:

1
2
3
4
search:
path: search.xml
# 如只想索引文章,可设置为post
field: all

实现搜索框视图

结构文件
search.ejs

1
2
3
4
5
6
7
8
9
10
<div class="search-box" id="search-box">
<form>
<div class="search-btn"></div>
<input autocomplete="off" type="text" id="search-input" name="q" results="0" placeholder="搜索"
/>
<button type="reset" class="reset-search-btn"></button>
</form>
<!-- 上面是搜索框,下面是结果域 -->
<article id="search-result" class="search-result content"></article>
</div>

样式文件
search.css

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
// 搜索框
.search-box {
position: absolute;
right: 40px;
top: 13px;
width: 40px;
height: 36px;
line-height: 1;
outline: none;
transition: width 0.1s;
}
.search-box-focus {
width: 164px;
padding-left: 38px;
background: #f0f1f2;
}
.search-box-focus #search-input {
display: block;
}
.search-box form {
display: block;
padding: 0;
margin: 0;
}
.search-box .search-btn,
.reset-search-btn {
position: absolute;
width: 30px;
height: 30px;
top: 3px;
border: none;
outline: 0;
background-size: 30px 30px;
background-repeat: no-repeat;
background-position: center;
background-color: initial;
}
.search-box .search-btn {
left: 3px;
background-image: url(../images/header_search.png);
}
.reset-search-btn {
right: 3px;
display: none;
background-image: url(../images/header_close.png);
}
.search-box-focus .reset-search-btn{
display: block;
}
#search-input {
display: none;
width: calc(100% - 36px);
border: none;
font: inherit;
font-size: 14px;
line-height: 1;
outline: none;
padding: 8px 0;
background: transparent;
}
#search-input::-webkit-input-placeholder {
color: #999;
}
.search-result ul, .search-result li {
list-style: none;
margin: 0;
padding: 0;
}
.search-result li {
padding-top: 30px;
}
.search-result li .search-result {
overflow : hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
.search-result {
display: none;
}
.search-result h1 {
padding-bottom: 20px;
margin-bottom: 0;
color: #333;
}
.search-result em {
color: #38f;
-webkit-text-emphasis: none;
}
.search-result h1 em {
margin: 0 5px;
}
.search-result h1.empty-title {
color: #999;
border-bottom: none;
padding-top: 200px;
text-align: center;
}

实现站内搜索逻辑

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
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
167
function searchFunc(path, searchId, contentId) {
$.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 $searchBox = $('#' + searchId);
if (!($searchBox && $searchBox.length == 1)) {
return;
}
var $resultContent = $('#' + contentId);
if (!($resultContent && $resultContent.length == 1 )) {
return;
}

var $input = $searchBox.find('#search-input');
var $searchBtn = $searchBox.find('.search-btn');
var $resetBtn = $searchBox.find('.reset-search-btn');

$searchBtn.on('click', function (e) {
$searchBox.addClass('search-box-focus');
$input[0].focus();
});
$resetBtn.on('click', function (e) {
$('#content').css({
display: 'block'
});
$resultContent.css({
display: 'none'
});
$input[0].focus();
});
$input.on('blur', function () {
($(this).val() === '') && $searchBox.removeClass('search-box-focus');
});
var flag = true;
$input.on('compositionstart', function () {
flag = false;
});
$input.on('compositionend', function () {
flag = true;
});
$input.on('keydown', function (e) {
var e = e || event;
if (e.keyCode === 13) {
e.returnValue = false;
e.preventDefault();
}
})
$input.on('input', function () {
var $this = this;
setTimeout(function () {
if (!flag) {
return;
}
var str = '<ul class="search-result-list">';
var keywords = $this.value.trim().toLowerCase().split(/\s+/);
$resultContent[0].innerHTML = '';
if ($this.value.trim().length <= 0) {
$('#content').css({
display: 'block'
});
$resultContent.css({
display: 'none'
});
return;
} else {
$('#content').css({
display: 'none'
});
$resultContent.css({
display: 'block'
});
}
var matchCount = 0;
datas.forEach(function (data) {
var isMatch = true;
if (!data.title || data.title.trim() === '') {
return false;
}
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;
}
}
});
} else {
isMatch = false;
}
if (isMatch) {
matchCount++;
str += '<li><h3><a target = "_blank" href="' + data_url + '" class="search-result-title">' + data_title + '</a></h3>';
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.substr(start, end);

// highlight all keywords
keywords.forEach(function (keyword) {
var reg = new RegExp(keyword, 'gi');
match_content = match_content.replace(reg, function (str) {
return '<em class="search-keyword">' + str + '</em>';
})
});

str += '<p class="search-result">' + match_content + '...</p>';
}
str += '</li>';
}
});
$input.attr('results', matchCount);
str += '</ul>';
var title = matchCount ? '<h1>找到<em>' + $this.value.trim() + '</em>相关内容' + matchCount + '个</h1>' : '<h1 class="empty-title">没有找到<em>' + $this.value.trim() + '</em>相关内容</h1>';
str = title + str;
$('#content').css({
display: 'none'
});

$resultContent.css({
display: 'block'
});
$resultContent[0].innerHTML = str;
}, 0);
});
}
});
};

将搜索功能添加如页面

将如下代码添加到合适的地方,一般为layout.ejs 最下方

1
2
3
4
5
6
7
8
9
10
11
<% if (config.search) { %>
<script type="text/javascript" src="/js/search.js"></script>
<script type="text/javascript">
var search_path = "<%= config.search.path %>";
if (search_path.length == 0) {
search_path = "search.xml";
}
var path = "<%= config.root %>" + search_path;
searchFunc(path, 'search-box', 'search-result');
</script>
<% } %>