title: 让 Hexo 博客支持本地站内搜索 date: 2016-05-31 13:50:19 categories: 术业专攻 tags: - Hexo - JavaScript - jQuery toc: permalink: hexo-local-search ---

前言

最近给 [Yelee](https://github.com/MOxFIVE/hexo-theme-yelee) 主题加上了本地搜索功能,终于能在自己的博客里愉快地搜索资料了。大致思路来源于 HaHack 的 [教程](http://hahack.com/codes/local-search-engine-for-hexo/),根据需要做了些调整。重新整理一下本地搜索折腾记录,分享于此。 --- ![Hexo Local Search](/resources/hexo-local-search.gif) ## 索引生成 要使用搜索,必须先生成博客索引数据,Hexo 可以通过插件生成: ```bash npm install --save hexo-generator-search ``` 插件默认只索引文章(post),要想页面(page)也能被检索,只需要在 Hexo 站点 `_config.yml` 中添加如下配置即可: ```yaml search: path: search.xml field: all ``` > 更多配置说明可到插件页面查看:[hexo-generator-search](https://github.com/PaicHyperionDev/hexo-generator-search) ## 界面结构 因为自己博客是双栏,顺手就把搜索结果放在边栏中了,大致 HTML 结构如下: ```html

No results found

``` > 搜索重置按钮使用了 [Font Awesome](http://fontawesome.io/) 图标,可按需更换 ## 功能代码 ### 基础代码 > 基础搜索函数 `(jQuery)` 来源于 HaHack 的教程,个人进行了些调整: 1.新标签中打开文章页面;2.减少截取的字符数;3.去掉部分非必要的代码 - 使用时将下边代码保存为 js 文件并在页面中的合适位置引入 ```js // A local search script with the help of [hexo-generator-search](https://github.com/PaicHyperionDev/hexo-generator-search) // Copyright (C) 2015 // Joseph Pan // Shuhao Mao // Edited by MOxFIVE 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='
    '; 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 += "
  • "+ "> " + data_title +""; 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, ""+keyword+""); }) str += "

    " + match_content +"...

    " } } }) $resultContent.innerHTML = str; }) } }) } ``` ### 调用函数 - `search.xml` 使用默认路径,可以直接把下面代码放到 js 文件中 ```js var path = "/search.xml"; searchFunc(path, 'local-search-input', 'local-search-result'); ``` - 上面的函数, `search.xml` 文件会跟随页面一起加载,如果索引文件太大,可能会影响页面加载速度,可以将其调整为`激活搜索框`时再下载所需文件 ```js var inputArea = document.querySelector("#local-search-input"); var getSearchFile = function(){ var path = "/search.xml"; searchFunc(path, 'local-search-input', 'local-search-result'); } inputArea.onfocus = function(){ getSearchFile() } ``` ### 搜索重置 - 提供按钮用于清空搜索结果和重置搜索框,按钮已经绑定了点击事件,直接写函数就行 ```js 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(); } ``` ### 屏蔽回车 - 虽然用了表单但其实并没有数据要提交,所以这里需要手动屏蔽掉回车键 ```js inputArea.onkeydown = function(){ if(event.keyCode==13) return false} ``` ### 无搜索结果 - 无搜索结果时,显示指定的提示内容。 - 原本想在基础搜索函数上改,折腾无果,只能曲线救国,通过监听搜索区内容变动来判断是否有匹配的内容 ```js $resultArea.bind("DOMNodeRemoved DOMNodeInserted", function(e) { if (!$(e.target).text()) { $(".no-result").show(200); } else { $(".no-result").hide(); } }) ``` ## CSS 样式 样式部分按自己喜好设计即可,下面是个人目前使用的样式,可参考 ```css /*搜索框*/ .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: #808080; } #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%; font-size: 0.9em; text-align: left; word-break: break-all; } #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(158,188,226,0.21); 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: #708090; } /*搜索预览段落*/ #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: #808080; } /*匹配的关键词*/ #local-search-result em.search-keyword { color: #f58e90; border-bottom: 1px dashed #f58e90; font-weight: bold; font-size: 0.85em; } /*无匹配搜索结果时显示*/ 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; } ``` ## 一些不足 > - [ ] 如果文章很多,索引文件可能很大,无论是随页面下载还是激活搜索框再下载,似乎都不理想; - [ ] 搜索结果中全部内容都转成了小写,应该有办法忽略大小写的同时,保持文本原始大小写格式; - [ ] 使用自动补全填上的内容并不能立即显示搜索结果,待改进。 ## 相关链接 1. ***jQuery-based Local Search Engine for Hexo*** by **HaHack** on 2015/10/08: 1. ***feat: Local Site Search | 本地站内搜索*** by **MOxFIVE** on 2016/05/25: 1. **hexo-generator-search**: