打造完美的 ajax 版 Google 自定义搜索

通常我们选用 Google CSE 自定义搜索引擎代替网站自身的搜索服务,可以减轻服务器的负载,但更重要的原因是 Google 搜索有强大的词语分割、智能匹配、拼写纠正功能,甚至能将 “bb” 与 “BlackBerry”, “DM” 与 “桌面管理器” 进行通配,这些算法是我们自己做不到的。我以前在博客中采用 iframe 版的 CSE, 最近把它换成了定制性更强,基于 Google ajax API 的新版,将经验分享一下。本方案优点:

  • 不搜索时完全不加载任何相关资源,如 ajax API 库等
  • 搜索 url 非常干净,没有多余的参数:fis.io/search?q=cse
  • 方便使用 javascript 对结果样式进一步调整

获取代码

首先需要在 控制面板 – 外观新功能!中选择“搜索元素”模式 (Search element), 再选择一种布局和一种样式。实际上 ajax API 能做的事情非常多,比如就在搜索框下方即时展开结果列表。如果把结果列表悬浮绝对定位,再加上 Search as user Types, 就可以做 apple.com 右上角那种搜索样式了,一边输入一边匹配。

但是考虑到小博客的站内搜索被使用得并不多,我还是选择了两栏布局,将结果列表放在一个专门的页面 /search 中,这样的好处是不搜索的时候可以不加载多余的内容:API 库,JS, CSS 等等。

改造搜索框

先不急着把获取的那一大堆代码往搜索框上放,上面说了,我们要的效果是不搜索的时候不加载。就改造一下模板原生的搜索框就可以了,让它提交用户输入的内容到 /search 这个页面,就这样。我的 header.php 中的搜索框是这样的:

<form action="/search" id="searchbox">
    <input type="text" name="q" id="input_search" />
    <input type="submit" value="搜索" />
</form>

一个 form 中装一个输入框和一个按钮,关键内容是 action=”/search” 和 name=”q”, 表示将会跳转到 /search?q=搜索的内容.

结果页面

建立一个页面 /search 来放入所有 CSE 代码。页面正文中放入 id=”cse” 的 div, 搜索执行的时候脚本将会改写其中的内容。

<div id="cse">正在搜索...</div>

然后在页面任意位置,比如末尾,写入前面获得的两个 javascript 代码,一个是 ajax API 库 google.com/jsapi, 另一个是以 google.load 开头的一大串。还有些 CSS, 是前面获取代码的时候选择的一种样式。

但这时候还不会自动执行用户之前输入的搜索,要从 url 请求中把搜索词剥离出来,并执行。这里我用了 Kevin Yang 提供的方法,在 draw(‘cse’) 一行后加入:

var match = location.search.match(/q=([^&]*)(&|$)/);
if(match && match[1]){
    var search = decodeURIComponent(match[1]);
    customSearchControl.execute(search);
}

现在这个搜索系统就可以正常工作了。下面是对脚本的进一步自定义,只逐条说明,不每次都写完整的代码,我会把完整的修改后的脚本放在本文末尾。如果今后代码发生变动,请自行查看我的 搜索结果页 源代码。

不加载 Google 的 CSS

如果要用自己网站的样式,最好是完全不加载 Google 的 CSS, 不然覆盖样式就有得写了。在 google.load 中加入 “nocss” : true 即可。

google.load('search', '1', {language : 'zh-CN' , "nocss" : true });

无结果时显示的文字

由 setNoResultsString 控制,在无结果时将字串写入到 “正在搜索” 处。中文语言下缺省值为“无结果”

customSearchControl.setNoResultsString('什么也没找到,请重试');

结果每页条数

由 setResultSetSize 控制,可选参数为 FILTERED_CSE_RESULTSET 10条;LARGE_RESULTSET 8条;SMALL_RESULTSET 4条。

customSearchControl.setResultSetSize( google.search.Search.SMALL_RESULTSET);

是否在新标签中打开链接

由 setLinkTarget 控制,一般用到的就是 LINK_TARGET_BLANK 和 LINK_TARGET_SELF 两种。

customSearchControl.setLinkTarget( google.search.Search.LINK_TARGET_SELF);

搜索执行完毕后调用其它脚本

由 setSearchCompleteCallback 控制,这是一个相当灵活的命令,我这里用它来将搜索结果标题中的“老肥博客 » 非唠不可”去掉,不然每条标题后面都有这样一句,比较难看。这里我另外加载了 jQuery 来用,当然这不是必需的,如果没有需要就不用了。

customSearchControl.setSearchCompleteCallback(null, function() {
    $('input.gsc-input').select();
    $('a.gs-title').unwrap().wrap('<h3></h3>').each(function() {
        var title = $(this).html().replace(/\|.*/g, '');
        $(this).html(title);
    });
    $('b:contains("...")').contents().unwrap();
});

以上在 setSearchCompleteCallback 中执行了三个步骤:

  • 将焦点放到搜索框 input.gsc-input 中并全选文字;
  • 将搜索结果标题链接 a.gs-title 先去掉外面的一层 div, 再套在 <h3> 内(这样方便沿用全局 CSS 里面的标题样式),然后对每个标题链接读取内容,替换,写回去;
  • 将包含 “…” 的关键字高亮去掉 <b>;(Google 用 <b> 标示关键字,但不知为何 “…” 也都这样标记)

其它

我的搜索页面中还有一些脚本和样式,比如将 CSE 的搜索框伪装成模板原生的输入框,这样可以避免多次搜索的时候重复加载页面;从搜索框中取得当前关键字,写入到右侧提示区;等等,比较特殊,这里就不细写了,反正 Firebug 什么的都一眼看穿。

另外,前面提到 Kevin 的文章中有个技巧很不错,只让单篇文章出现在搜索结果中,排除掉翻页、标签等页面,像我用 .html 作为单篇文章的链接结构就很好办,直接在 CSE 控制面板中设置“包含的网站”为 fis.io/*.html, 就排除了其它形式的链接结构。

微博也是博客,我在 fis.io/*.html 之外还将 twitter.com/fisio/* 编入了索引,在搜索某些内容的时候会看到我的推也在搜索结果中。

最后是我的 /search 页面中相关脚本完整版:

<script src="https://www.google.com/jsapi" type="text/javascript"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
google.load('search', '1', {language : 'zh-CN' , "nocss" : true });
google.setOnLoadCallback(function(){
    var customSearchControl = new google.search.CustomSearchControl('015811090669888844099:szzhafqj8_4');
    customSearchControl.setResultSetSize(google.search.Search.SMALL_RESULTSET);
    customSearchControl.setLinkTarget(google.search.Search.LINK_TARGET_SELF);
    customSearchControl.setNoResultsString('<img src="/b.gif" class="wp-smiley sm-sad">  什么也没找到,请重试');
    customSearchControl.setSearchCompleteCallback(null,function() {
        $('input.gsc-input').select();
        var searchwords = $('input.gsc-input').val();
        $('.p > b').text(searchwords);
        $('a.gs-title').addClass('new').unwrap().wrap('<h3></h3>').each(function() {
            var title = $(this).html().replace(/\|.*/g, '');
            $(this).html(title);
        });
        $('b:contains("...")').contents().unwrap();
        $('.gsc-cursor-current-page').removeClass('gsc-cursor-page');
    });
    customSearchControl.draw('cse');
    var match = location.search.match(/q=([^&]*)(&|$)/);
    if(match && match[1]){
        var search = decodeURIComponent(match[1]);
        customSearchControl.execute(search);
    }
});
</script>

参考文献

最新评论

  • fisio 可不,Google Reader 都去世十年了… Google Reader 的分享和效率
  • 轻重 天哪!今天整理Evernote,发现这条2010年的剪藏笔记,点击连接,原网站还在!12年了,中文互联网早已面目全非! Google Reader 的分享和效率
  • 小波 重新改博客,看看以前的wp小伙伴的博客,发现绝大部分都关了,没想到你的还在,只是停在11年了 我为什么爱 Twitter
  • 心灵博客 当年有这个效果已经很不错哦 diy 山寨投影家庭影院
  • fisio 可不咋地!您也是老司机了 我为什么爱 Twitter

发表您的评论

评论将回复给

😀🙂😯😁🙁😄😆😝😜👍🌈💖🎂

目前有 187 条评论

    1. 我觉得这样方便阅读些。。

      今天把文章发出来才临时加上的 white-space: nowrap 和 overflow-x: auto, 回头再斟酌一下 😛

      1. 我觉得可以在hover的时候把<code>设为float:left
        不过缺点是要紧跟清除浮动的标签,而且如果代码真的很长的话还是会有些问题

        1. 谢谢您的意见,float 这样好多了,边框一起变宽了。

          我一开始就单独设了个 class 来做这个 hover, 就是担心以前文章里面的代码搞出问题,这样就不用去复查以前贴的代码了 😀

  1. 好,我去把我的搜索框修改一下,问题是,搜索框里面有个背景字啊,custom srearch,这个这么去掉啊,我看有点博客搜索框没这个字啊。

  2. 给你的i[at]fls.io发邮件,是不是没收到啊?

    问一下你的主题中,CSS中的当前页面(class:current_page)是如何把“存档”给做进去的?比如,点击你任一篇文章,都会发现,“订阅”页面被标记为当前页面,that’s
    cool!这是如何实现的呢?谢谢。

    1. 那只是个 css 样式,应用于 a class=”external”, 有个右边的 padding, 然后应用了一个背景图片 😀

    1. 参考一下我的代码,就是这行:

      var title = $(this).html().replace(/\|.*/g, ”);

      将标题读取了之后,进行正则替换,扔掉了第一个 “|” 之后的所有内容,如果你的标题格式并不是这样,改成自己的就好了,| 前面的 \ 是转义符。

      1. 代码比较好几次了,如果我眼睛无码应该没问呀。我的博客不是放在根目录而是xx.com/blog/这样子的,是不是这个和那个正则表达“var match = location.search.match(/q=([^&]*)(&|$)/);”式有关系?

        1. 应该是这一步的原因,因为从你的搜索结果页面上的 google 搜索框去搜索中文是正常的.. 😮

          试试看在 /q 之前加上 /blog 呢?

    1. 看本页的源代码,里面引用了 jquery, 还有一段代码是滚动用到的,源代码里搜索一下 toppadding 就找到啦

    1. 翻页按钮是自带的,我只是调过了样式,如果有更多的搜索结果,好像是另外打开一个 google 的页面来显示,也可以关闭掉这个外链

  3. 您好,看了好多人做的Google自定义搜索,均没有完美解决问题,你的搜索看起来解决了许多问题,我研究了很久依然没成功,主要是样式的定义,无法控制外观,出现很多问题,可否把你的搜索的相关文件发到我QQ邮箱:394731189@qq.com
    我好研究一下,非常感谢

      1. 跟其他人一样,用中文传参数会报错:
        解码的url不合法
        解决方法是将页面编码变成utf-8
        另外一个问题是,你试着用ie浏览器浏览下,就会发现,你的搜索框在输入一次内容之后缩小成只容一个字符那样的宽度,解决的方法是,在你的css文件中加入如下这行:
        .gsc-input{width:155px;}
        定义他的宽度。

  4. 其实还存在一个问题,你用ie浏览器搜索会发现,第一条搜索结果的标题没有显示,
    我改了你自己定义的一些jQuery,我也不知道咋意思,不过这问题被我稀里糊涂的解决了。

  5. 其实还有个致命的漏洞,你在你的博客搜索
    字母”b” 这个关键字看看
    你再测试一下分页,你点下第七页,就会发觉
    本来只有八页,现在变成了只有两页而已。请问博主,这个问题如何解决? 8)

    1. 哈哈哈,貌似评论重复了,因为我点第一次没反应,关于输入b翻页问题,其实是Google自己的问题,这问题我测试过了,的确是Google自己的问题,可能有人不相信Google这大公司也有这种错误,其实还有一个乱码的问题,Google自己的网站上也是出现这个问题。

  6. 这么好的博客,我从你这至少都取得了2名访客的回访了。不知道您是不是最近过于太忙的缘故,最近您好像都不怎么更新文章了。

  7. 学习了!!!但请问有没有办法让出来的结果的链接不是通过google的url跳转阿?遇到敏感词时一跳转就被墙,貌似90秒左右

  8. 请教站长,为什么我按照以下步骤,都没有成功呢?
    1.把“searchform”搜索框代码更改你上述的代码
    2.建立 cse.php文件上传,并建立search页面【导入cse.php模板】,cse.php模板的代码是最后/search的完整版;
    结果一点效果都没有。。。我不太懂代码,我上面这样的步骤有什么不对的吗?
    对了,你的固定链接是怎么设置的?

    麻烦站长答复,非常感谢!

    1. 为什么没有成功。。这个真回答不了,找个熟悉你代码的人帮你看吧。固定链接的设置在 WordPress 设置里有

      1. 谢谢博主的回答。
        能否问2个和代码没有关的问题?
        1.我的2个步骤没有出错吧?
        2.除了你上述的步骤,还需要做其他设置吗?

      2. 呵呵,不好意思,再请教个非代码问题
        是不是要申请Google AJAX Search API key,这些代码才能生效呢?

  9. 我仔细看了你写的,听着很高深的样子,然后,我试了一下你右上角的那个谷歌搜索,然后,,,然后一直等待。。。我重新刷新了页面,又欢乐个词搜索,然后,,,还是等待,,,




还有 7 条 pingbacks 从其它网站引用了本文