サイト制作に関するメモ書き

HOME > JavaScript > jQuery > jQueryで実装したキーワード検索にページ送りを追加する

jQueryで実装したキーワード検索にページ送りを追加する

以前jQueryを使ってキーワード検索の機能ページ送りの機能を実装しましたが、今回はその2つを組み合わせてみます。

サンプルコード

HTMLには検索フォームと検索結果を追加する要素、ページ送りを追加する要素を設置します。

<form action="./" method="get">
  <input type="search" name="keyword" id="keyword">
  <button>送信</button>
</form>

<div id="js-search-result"></div>
<div id="js-pager"></div><p></p>

CSSは今回最低限の見た目を整えているのみなので省略します。
最後にJavaScriptですが、今回の組み合わせに合わせて変数や関数を整理したり、細かい部分で色々と変更していますが、大きな流れはほぼ変更ありません。

var searchOptions = {
  paramKey: 'keyword', // 検索キーワードとして取得するパラメータのキー
  jsonKeys: ['title', 'categories'],  // 検索対象にするjson内のキー
  output: '#js-search-result' // 検索対象にするjson内のキー
};
var pagerOptions = {
  paramKey: 'page', // 何ページ目かを示すパラメータのキー
  articleCount: 3, // 1ページあたりに表示させる記事の件数
  pagerMaxCount: 3, // ページ送りの最大数
  output: '#js-pager' // ページ送りの出力先
};
var jsonPath = 'data.json'; // 記事情報のjsonのパス
var params;

$(function(){
  // URLからキーワードとページ番号を取得
  params = get_parameters();
  var s = get_search_keywords(params, searchOptions['paramKey']);
  var currentNumber = get_current_page_number(params, pagerOptions['paramKey']);

  if(s) {
    // ajaxで記事情報を取得
    $.ajax({
      url: jsonPath,
      cache: false
    })
    .then(
      function (data) {
        // キーワードに一致する記事情報のインデックスを取得
        var matchData = keyword_search(data, s, searchOptions['jsonKeys']);
        if(matchData.length > 0) {
          // 検索結果一覧とページ送りを生成
          generate_result(matchData, currentNumber);
          generate_pager(matchData, currentNumber);
        } else {
          add_message_to_search_result('キーワードに一致する記事がありませんでした。');
        }
      },
      function () {
        add_message_to_search_result('取得に失敗しました。');
      }
    );
  } else {
    add_message_to_search_result('キーワードが入力されていません。');
  }
});

/**
 * URLからパラメータを取得して配列形式で返す
 */
function get_parameters() {
  // URLからパラメータ取得
  var params = [];
  var param = location.search.substring(1).split('&');
  for(var i = 0; i < param.length; i++) {
    params[i] = param[i].split('=');
  }
  return params;
}

/**
 * 検索に使用するキーワードを取得する
 * キーワードがない場合はfalseを返す
 * @param {string} params (required) パラメータ
 * @param {string} key    (required) パラメータのkey
 */
function get_search_keywords(params, key) {
  // キーワードを配列形式で格納
  var keywords = [];
  var separator =(/ | |\+/g);
  for(var i = 0; i < params.length; i++) {
    if(params[i][0] === key && params[i][1] !== undefined) {
      keywords = decodeURIComponent(params[i][1]).split(separator);
      break;
    }
  }
  // キーワードの値が空のものを除去
  keywords = keywords.filter(function(e){ return e !== ''; });
  // キーワードがない場合はfalseを返す
  if(keywords.length <= 0) {
    return false;
  }
  // キーワードを小文字に変換
  for(var i = 0; i < keywords.length; i++) {
    keywords[i] = keywords[i].toLowerCase();
  }
  return keywords;
}

/**
 * ページャーの現在のページ数を取得する
 * @param {string} params (required) パラメータ
 * @param {string} key    (required) パラメータのkey
 */
function get_current_page_number(params, key) {
  var current = 1; // パラメータにページ数がない場合は1ページ目にする
  for(var i = 0; i < params.length; i++) {
    if(params[i][0] === key) {
      current = parseFloat(params[i][1]);
      break;
    }
  }
  return current;
}

/**
 * 記事内のキーワード検索
 * @param {object} articleData (required) 検索する記事情報
 * @param {array}  keywords    (required) 検索するキーワード
 * @param {array}  jsonKeys    (required) 検索対象にする記事情報のキー
 */
function keyword_search(articleData, keywords, jsonKeys) {
  var data = articleData['data'];
  var h = [];
  // 検索対象の値を配列にまとめる
  for (var i = 0; i < data.length; i++) {
    var v = [];
    for (var j = 0; j < jsonKeys.length; j++) {
      var thisVal = data[i][jsonKeys[j]];
      // 値が配列の場合はその各値を取得
      if(Array.isArray(thisVal)) {
        for (var k = 0; k < thisVal.length; k++) {
          v.push(thisVal[k].toLowerCase());
        }
      } else {
        v.push(thisVal.toLowerCase());
      }
    }
    h.push(v);
  }

  // 一致する配列のindexを取得
  var matchData = [];
  var matchCount;
  var thisArr;
  // 各記事のループ
  for (var i = 0; i < h.length; i++) {
    matchCount = 0;
    thisArr = h[i];
    // 検索キーワードでのループ
    for (var j = 0; j < keywords.length; j++) {
      // 記事の各項目でのループ
      for (var k = 0; k < thisArr.length; k++) {
        // 記事項目内に検索キーワードが含まれる場合
        if(thisArr[k].indexOf(keywords[j]) > -1) {
          matchCount++;
          break;
        }
      }
      // 検索キーワードが各項目に含まれなかった場合
      if(matchCount <= j) {
        break;
      }
      // 検索キーワードが全て記事に含まれていた場合
      if(matchCount >= keywords.length) {
        matchData.push(data[i]);
      }
    }
  }
  return matchData;
}

/**
 * 検索に一致した記事で検索結果を生成
 * @param {object} articleData (required) 記事情報
 * @param {array}  current     (required) 現在のページ数
 */
function generate_result(articleData, current) {
  var data = articleData;
  var first = (pagerOptions['articleCount'] * (current - 1));
  var ins = '';

  for (var i = first; i < Math.min(first + pagerOptions['articleCount'], data.length); i++) {
    ins += '<div class="article">';
      ins += '<h2 class="article_ttl">';
        ins += data[i]['title'];
      ins += '</h2>';
      ins += '<ul class="article_categories">';
        for(var j = 0; j < data[i]['categories'].length; j++) {
          ins += '<li>' + data[i]['categories'][j] + '</li>';
        }
      ins += '</ul>';
    ins += '</div>';
  }
  $(searchOptions['output']).html(ins);
}

/**
 * ページャーの表示
 * @param {object} articleData (required) 記事情報
 * @param {array}  current     (required) 現在のページ数
 */
function generate_pager(articleData, current) {
  var data = articleData;
  var articleCount = data.length;
  var pagerLength = Math.ceil(articleCount / pagerOptions['articleCount']);
  var pagerVisibleCount = pagerOptions['pagerMaxCount'];
  var baseUrl = './?';
  for(var i = 0; i < params.length; i++) {
    if(params[i][0] !== pagerOptions['paramKey']) {
      baseUrl += params[i][0] + '=' + params[i][1] + '&';
    }
  }
  baseUrl += pagerOptions['paramKey'] + '=';

  // 記事が0件の場合またはページャーが1件のみの場合は表示しない
  if(pagerLength <= 1) {
    return;
  }
  // 現在のページが存在しない場合は1ページ目にリダイレクト
  if(current > pagerLength) {
    location.href = baseUrl + 1;
  }
  var startNumber = 1;
  if(pagerLength <= pagerOptions['pagerMaxCount']) {
    pagerVisibleCount = pagerLength;
  } else if(current <= Math.ceil(pagerOptions['pagerMaxCount'] / 2)) {

  } else if(current > pagerLength - Math.ceil(pagerOptions['pagerMaxCount'] / 2)) {
    startNumber = pagerLength - pagerOptions['pagerMaxCount'] + 1;
  } else {
    startNumber = current - Math.ceil(pagerOptions['pagerMaxCount'] / 2) + 1;
  }
  var ins = '';
  ins += '<div class="pager">';
    if(current !== 1) {
      ins += '<a class="pager_first" href="' + baseUrl + '1' + '">first</a>';
      ins += '<a class="pager_prev" href="' + baseUrl + (current - 1) + '">prev</a>';
    }
    ins += '<ol class="pager_list">';
    for (var i = startNumber; i < startNumber + pagerVisibleCount; i++) {
      if(i === current) {
        ins += '<li class="pager_item is-current"><a href="' + baseUrl + i + '">' + i + '</a></li>';
      } else {
        ins += '<li class="pager_item"><a href="' + baseUrl + i + '">' + i + '</a></li>';
      }
    }
    ins += '</ol>';
    if(current !== pagerLength) {
      ins += '<a class="pager_next" href="' + baseUrl + (current + 1) + '">next</a>';
      ins += '<a class="pager_last" href="' + baseUrl + pagerLength + '">last</a>';
    }
  ins += '</div>';
  $(pagerOptions['output']).html(ins);
}

/**
 * 検索結果部分にメッセージを表示
 * @param {object} message (required) 表示するメッセージの内容
 */
function

add_message_to_search_result(message) {
  $(pagerOptions['output']).html('<p>' + message + '</p>');
}

「マンガ」で検索した場合のデモページ

一番大きな変更点としては、元々の検索機能では検索キーワードに一致する記事のインデックス番号を抽出して、HTML生成時に記事情報と紐づけて生成するようにしていました。
ただ今回は1ページあたりの表示数が制限されるなどの理由もあり、検索キーワードと一致する記事を探す段階で検索結果用の記事情報一覧を別途生成する仕様に変更しています。
 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

▲PAGE TOP