Pull to refresh

Делаем client side поиск для статического блога



Я люблю статические блоги. Но очень не люблю то, как в них часто устроен поиск. А именно то, что в большинстве таких блогов по запросу в поисковой строке просто открывается google. В новой вкладке, если повезет. Такая ситуация меня не устраивает, и я подумал об организации нормального поиска в своем блоге на Octopress.

Перед тем, как строить свой велосипед, надо сначала изучить, какие уже придуманы.

Tapir


Tapir — бесплатный сервис, индексирующий RSS ленту Вашего блога, и отдающий результаты поиска по запросу.

Плюсы:
+ Бесплатен
+ Не навязывает свои кнопки стили
+ Быстр
+ Легко интерпретировать

Минусы:
— Не работает с русским языком

Для меня не работа с русским языком — критический недостаток, поэтому от Тапира пришлось отказаться.

Google Custom Search


Google Custom Search — система, позволяющая настроить на Вашем сайте поиск через Google. Мне очень понравился у них вид «Накладка», в котором результаты поиска открываются в модальном окне поверх сайта.

Плюсы:
+ Очень легко интерпретировать
+ Дизайн поиска настраивается из личного кабинета

Минусы:
— HTML и CSS подгружается с серверов Google. Хотя изменить с помощью CSS/JS можно.
— Поиск Google (Я не силен в SEO, поэтому там много лишнего)
— Реклама

Лично мне этот поиск тоже не подходит.

Если хочешь сделать что-то хорошо — сделай это сам.


Ибо больше готовых решений я не нашел, решил написать свою балалайку.

Итак, у меня блог на Octopress, с темой Octostrap 3 (BootStrap 3)

Идея проста: пользователь делает запрос, всплывает модальное окно (BootStrap 3) и происходит поиск запроса по atom.xml файлу. Затем результат выводится в виде записей.

По умолчанию в octopress стоит лимит на 20 записей в RSS. Если мы хотим искать по всему блогу, то лимит стоит снять, убрав limit: 20 в atom.xml.

Для удобной работы с atom.xml подключим jFeed. (Предварительно скачав его)
<script src="{{ root_url }}/javascripts/jquery.jfeed.js"></script>


Добавляем модальное окно. (Я его добавил в after_footer)

<div class="modal fade" id="searchModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
          <h4 class="modal-title">Результаты поиска</h4>
        </div>
        <div class="modal-body">
        	<img src="/images/ajax-loader.gif" id="search-loading"></img>
        </div>
      </div><!-- /.modal-content -->
    </div><!-- /.modal-dialog -->
  </div><!-- /.modal -->

Где ajax-loader.gif:

Добавляем поисковой строке id:
<input class="form-control" type="text" id="query" placeholder="Поиск...">


И теперь javascript.
$("#search").submit(function( event ) {
  		event.preventDefault();
                $(".modal-body").html('<img src="/images/ajax-loader.gif" id="search-loading"></img>');
                var string = $("#query").val(); 

                // При пустом запросе и при запросе, в котором менее 4 символов, выводим сообщение об ошибке.
  		if (string.length == 0) {
  			$('#searchModal').modal('show');
  			$(".modal-body").html('<div class="alert alert-danger">Пустой запрос</div>');
  			return;
  		}
  		if (string.length < 4) {
  			$('#searchModal').modal('show');
  			$(".modal-body").html('<div class="alert alert-danger">Слишком корткий запрос</div>');
  			return;
  		}

  		var html = "";

  		$('#searchModal').modal('show');

               // Получаем RSS 
  		jQuery.getFeed({
	       url: '{{ root_url }}/atom.xml',
	       success: function(feed) {
	       		for(var i = 0; i < feed.items.length; i++) {
	       			
	       			var item = feed.items[i];
      
                                // Убираем все html теги.
	       			item.description = item.description.replace(/\<.*?>/g, '');

                               // Ищем по описанию и названию
	       			if (item.description.toLowerCase().indexOf(string.toLowerCase()) !== -1 || item.title.toLowerCase().indexOf(string.toLowerCase()) !== -1)  {
	       				html += '<div class="panel panel-default search-panel"> <div class="panel-body"> <div class="entry-title"><h3><a href="' + item.link + '">' + item.title + '</a></h3></div>';

						// Если ключевое слово встречается в описании, выводим его + по 30 символов слева/справа от него. Причем делаем ключевое слово выделенным
						if (item.description.toLowerCase().indexOf(string.toLowerCase()) !== -1) {
							html += '<hr /><div>...' + item.description.substring(item.description.toLowerCase().indexOf(string.toLowerCase()) -30 ,item.description.toLowerCase().indexOf(string.toLowerCase())) + '<strong>' + item.description.substring(item.description.toLowerCase().indexOf(string.toLowerCase()),item.description.toLowerCase().indexOf(string.toLowerCase()) + string.length) + '</strong>' + item.description.substring(item.description.toLowerCase().indexOf(string.toLowerCase()) + string.length,item.description.toLowerCase().indexOf(string.toLowerCase()) + string.length + 30) + '...</div>';
						}


                                                // Показываем в углу дату
						html += '<p class="text-muted"><span class="glyphicon glyphicon-calendar"></span>' + dateParse(item.updated) + '</p></div></div>'
	       			}
	                
	                
	            }
	            if (html == "") {

                        
	            	html += '<div class="alert alert-warning">Ничего не найдено</div>';
	            }
	            $(".modal-body").html(html);

	       }
		});

 
 });


Вспомогательная функция, которая возвращает дату в людском формате.
	function dateParse(date) {
		var year = date.substring(0,4);
		var month = date.substring(5,7);
		var day = date.substring(8,10);

		
		
		switch (month) {
			case '01':
				month = 'Января '
				break
			case '02':
				month = 'Февраля '
				break
			case '03':
				month = 'Марта '
				break
			case '04':
				month = 'Апреля '
				break
			case '05':
				month = 'Мая '
				break
			case '06':
				month = 'Июня '
				break
			case '07':
				month = 'Июля '
				break
			case '08':
				month = 'Августа '
				break
			case '09':
				month = 'Сентября '
				break
			case '10':
				month = 'Октября '
				break
			case '11':
				month = 'Ноября '
				break
			case '12':
				month = 'Декабря '
				break
		}

		switch (day) {
			case '3':
				day = day + 'ее '
				break
			default:
				day = day + 'ое '
		}
		return day + month + year + ' г.';
		
	}


Немного CSS:
.search-panel .text-muted {
  float: right; 
  padding-top: 10px;
}
.search-panel h3 {
  margin-top: 0;
}
#search-loading {
  display: block;
  margin: 0 auto;
}


Вот что получилось в итоге:


Конечно не хватает семантики, но мне пока такое решение нравится.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.