Hexo添加站内本地搜索

前言

  虽然 Shaun 博客目前数量不多,质量也不高,但抱着搞事的心态,先弄它一个站内本地搜索再说。

准备篇

  要想使用本地搜索功能,首先需要安装相应的搜索插件 hexo-generator-searchdb,网上可能大多数用的是 hexo-generator-search 这个插件,也有都装的,但 Shaun 就只安装这一个了,好像 hexo-generator-searchdb 更完善一点,由于 Shaun 前端接触的极少,所以就没有一一对比了,网上也没查到具体对比情况,有兴趣的童靴可以试试 (╯▽╰)。至于具体安装如下,在站点根目录执行:

1
npm install hexo-generator-searchdb --save

  安装完之后重新生成页面,将会发现 public文件夹 下多出一个 search.xml 文件。然后在配置文件 _config.yml 中添加:

1
2
3
4
# 站点本地搜索
search:
path: search.xml
field: all

其中:

  • path - 指定生成的索引数据的文件名。默认为 search.xml 。
  • field - 指定索引数据的生成范围。可选值包括:
    • post - 只生成博客文章(post)的索引(默认);
    • page - 只生成其他页面(page)的索引;
    • all - 生成所有文章和页面的索引。

  至于是在 主题配置文件,还是在 站点配置文件 中添加,个人觉得都没关系,附:Shaun 是在主题配置文件中添加的。

接下来就需要修改原主题的代码了。

改码篇

  由于 Shaun 博客主题是基于 SPFK 对照着 black-blue 进行修改的,而且因为 black-blue 是有搜索的(Shaun 不知道 black-blue 主题的作者是如何完成的,借助了什么技术),所以 Shaun 就看 black-blue 的搜索功能是修改了 SPFK 哪个地方,再将相应的代码添加至 SPFK 中(其中相应的代码来自让 Hexo 博客支持本地站内搜索),从而逐渐完成本次搜索功能。

首先找到 spfk 主题下的 left-col.ejs 文件,对其修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
<% if (theme.search_box){ %>
<!-- <form>
<input type="text" class="st-default-search-input search" id="search" placeholder=" Search...">
</form> -->

<form id="search-form"> <!-- 搜索框相关 -->
<input type="text" id="local-search-input" name="q" results="0" placeholder="Search..." class="search form-control" autocomplete="off" autocorrect="off"/>
<i class="fa fa-times" onclick="resetSearch()"></i> <!-- 清空/重置搜索框 -->
</form>
<div id="local-search-result"></div> <!-- 搜索结果区 -->
<p class='no-result'>No results found </p> <!-- 无匹配时显示,注意请在 CSS 中设置默认隐藏 -->
<%}%>

其次找到 spfk 主题下的 after-footer.ejs 文件,将其修改如下:

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
<% if (theme.search_box){ %>
<!-- <script type="text/javascript">
window.onload = function(){
document.getElementById("search").onclick = function(){
console.log("search")
search();
}
}
function search(){
(function(w,d,t,u,n,s,e){w['SwiftypeObject']=n;w[n]=w[n]||function(){
(w[n].q=w[n].q||[]).push(arguments);};s=d.createElement(t);
e=d.getElementsByTagName(t)[0];s.async=1;s.src=u;e.parentNode.insertBefore(s,e);
})(window,document,'script','//s.swiftypecdn.com/install/v2/st.js','_st');

_st('install','A1Pz-LKMXbrzcFg2FWi6','2.0.0');
}
</script> -->

<script type="text/javascript">
// 激活搜索框时才搜索
var inputArea = document.querySelector("#local-search-input");
var getSearchFile = function(){
// 调用搜索函数
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');
}
inputArea.onfocus = function(){ getSearchFile() }

// 搜索重置
var $resetButton = $("#search-form .fa-times");
var $resultArea = $("#local-search-result");
inputArea.oninput = function(){ $resetButton.show(); }
resetSearch = function(){
$resultArea.html("");
document.querySelector("#search-form").reset();
$resetButton.hide();
$(".no-result").hide();
}

// 屏蔽回车
inputArea.onkeydown = function(){ if(event.keyCode==13) return false}

// 无搜索结果
$resultArea.bind("DOMNodeRemoved DOMNodeInserted", function(e) {
if (!$(e.target).text()) {
$(".no-result").show(200);
} else {
$(".no-result").hide();
}
})

// 搜索函数
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);
var $resultContent = document.getElementById(content_id);
$input.addEventListener('input', function(){
var str='<ul class=\"search-result-list\">';
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 = [];
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 titles and contents
if(data_title != '' && 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;
}
}
});
}
// show search results
if (isMatch) {
str += "<li><a href='/"+ data_url +"' class='search-result-title' target='_blank'>"+ "> " + data_title +"</a>";
var content = data.content.trim().replace(/<[^>]+>/g,"");
if (first_occur >= 0) {
// cut out characters
var start = first_occur - 6;
var end = first_occur + 6;
if(start < 0){
start = 0;
}
if(start == 0){
end = 10;
}
if(end > content.length){
end = content.length;
}
var match_content = content.substr(start, end);
// highlight all keywords
keywords.forEach(function(keyword){
var regS = new RegExp(keyword, "gi");
match_content = match_content.replace(regS, "<em class=\"search-keyword\">"+keyword+"</em>");
})
str += "<p class=\"search-result\">" + match_content +"...</p>"
}
}
})
$resultContent.innerHTML = str;
})
}
})
}
</script>
<%}%>

最后找到 spfk 主题下的 main.styl 文件,在其末尾添加:

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
/*搜索框*/
.search {
width: 68%;
height: 18px;
margin-top: 1px;
padding: 0;
font-family: inherit;
border: 2px solid transparent;
border-bottom: 2px solid #d3d3d3;
border-radius: 2px;
opacity: 0.65;
background: none;
}
.search:hover {
border: 2px solid #d3d3d3;
opacity: 1;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
}

/*搜索重置按钮*/
#search-form .fa-times {
display: none;
padding: 1px 0.7em;
box-shadow: 0 0 3px rgba(0,0,0,0.15);
cursor: pointer;
color: #4094c7;
}
#search-form .fa-times:active {
background: #d3d3d3;
}
#search-form .fa-times:hover {
zoom: 1.1;
padding: 1px 0.6em;
border: 1px solid #d3d3d3;
box-shadow: 0 0 6px rgba(0,0,0,0.25);
}

/*搜索结果区*/
#local-search-result {
//margin: auto -12% auto -6%;
margin: 0;
font-size: 0.9em;
text-align: left;
word-break: break-all;

box-shadow: 4px 4px 6px rgba(0,0,0,0.46);
}

#local-search-result ul.search-result-list li:hover {
font-weight: normal;
}

/*单条搜索结果*/
#local-search-result li {
margin: 0.5em auto;
border-bottom: 2px solid #d3d3d3;
}
#local-search-result .search-result-list li:hover {
background: rgba(47,46,46,0.8);
box-shadow: 0 0 5px rgba(0,0,0,0.2);
}

/*匹配的标题*/
#local-search-result a.search-result-title {
line-height: 1.2;
font-weight: bold;
color: #4094c7;
}

/*搜索预览段落*/
#local-search-result p.search-result {
margin: 0.4em auto;
line-height: 1.2em;
max-height: 3.6em;
overflow: hidden;
font-size: 0.8em;
text-align: justify;
color: #ffffffb3;
}

/*匹配的关键词*/
#local-search-result em.search-keyword {
color: #f58e90;
border-bottom: 1px dashed #f58e90;
font-weight: bold;
font-size: 1em;
}

/*无匹配搜索结果时显示*/
p.no-result {
display: none;
margin: 2em 0 2em 6%;
padding-bottom: 0.5em;
text-align: left;
color: #808080;
font-family: font-serif serif;
border-bottom: 2px solid #d3d3d3;
}

  这里请注意,当对 main.styl 文件做以上修改时,可能会发现有两个 .search 样式,而且相差不大,这时,不要对其原有的 .search 进行修改,更不要去注释掉它,只做上述修改就不用管了,不然可能会发生一些奇怪的事 o(>﹏<)o。Shaun 当时做以上修改时,将其原有的 .search 样式注释掉之后,整个页面的 css 布局全部都乱了 (╯﹏╰),不知道为什么 (⊙_⊙?),这两个同名样式看起来明明差不多的,最后只能维持现状了,等以后有机会再看看吧,业余前端伤不起啊! ╮(╯_╰)╭。

  至此整个站内本地搜索功能基本完成,勉强可以使用站内搜索功能了。

问题篇

  *注:以下问题于 2018-03-02 都已经解决 ╮(╯▽╰)╭。

1、搜索函数返回的 url 地址有问题。

  问题描述:当点击搜索结果时,新弹出的标签页地址栏中 url 地址会有部分乱码情况;当鼠标移到搜索的结果列表上时,浏览器左下角显示的 url 地址虽然没有乱码情况,但其中有一个重复的/符号。所幸这两个问题并没有造成浏览器解析错误,浏览器还是可以正常显示页面的。

================= 修改日期:2018-03-02 =================

  解决办法:\blog\node_modules\hexo-generator-searchdb\templates\xml.ejs 文件中的 <url><%- encodeURIComponent(config.root + post.path) %></url> 修改为 <url><%- encodeURI(post.path) %></url> ,使其中一些 url 中常见的字符(如:&, ?, /, =)不被十六进制的转义序列进行替换。

参考:escape,encodeURI,encodeURIComponent有什么区别?JavaScript encodeURIComponent() 函数

=====================================================

2、搜索结果区布局有问题。

  问题描述:当显示搜索结果时,搜索结果区会上下扩张,从而将其上下本来存在的一些布局挤开,造成布局混乱。这其实不算是一个 spfk 主题或者新添加的搜索功能的问题,而是新添加的一个东西又没有相应的和原本布局结合的布局文件,那就极大可能会有布局混乱的问题,至于这个要和原本布局契合的搜索结果区布局文件就只有等 Shaun 以后有机会有时间再完善去喽 ╮(╯▽╰)╭。

3、搜索框激活问题。

  问题描述:搜索框激活延迟很大,有时过很久或者需要切换站内页面它才能激活,给人的感觉就是好像没有搜索功能似的。添加搜索框激活功能据作者 MOxFIVE 所说是为了不让索引文件影响页面加载速度,MOxFIVE 同时也在文末指出了一些不足之处,如果索引文件太大,可能还是会造成一些问题,但 Shaun 的博客数量又不多,所以估计还是 Shaun 的代码混合问题,而且 MOxFIVE 的博客搜索功能好像没这个问题(至少 Shaun 目前没发现)。这个问题同样只有等以后再说了 (*^__^*) 嘻嘻……。

后记

  本文添加的本地搜索还很粗糙,还有很多地方需要以后去完善。但这好歹是一个好的开始,搜索功能至少勉强能够正常使用,总比以前是个空壳要好,以后有机会再慢慢去去完善吧 ↖(^ω^)↗。

参考资料

[1] jQuery-based Local Search Engine for Hexohttp://www.hahack.com/categories/codes/

[2] 让 Hexo 博客支持本地站内搜索http://moxfive.xyz/tags/Hexo/

[3] Hexo博客添加站内搜索https://www.ezlippi.com/categories/hexo/

[4] Hexo本地搜索及部分SEO优化https://www.oyohyee.com/categories/Note/