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ページあたりの表示数が制限されるなどの理由もあり、検索キーワードと一致する記事を探す段階で検索結果用の記事情報一覧を別途生成する仕様に変更しています。
 

このエントリーをはてなブックマークに追加

関連記事

4件のコメント

  1. takizawa より:

    cly7796.net様
    お世話になっております。takizawaです。
    早速、サンプルで試してみたところ、100%私が希望するスクリプトでした!ありがとうございました!
    キーワード検索ができるスクリプトは他サイトでも公開されているのですが、私の場合カテゴリが相当数ありますのでデータが膨大化した場合、1つのJSONファイルでの管理、または1ページ(1画面)に数十件、または数百件が表示され、スクロールするのには限界があると感じていました。この度の「複数データから同時取得」&「ページ送り機能」が実現できて、とても感動しています。
    現在、4つのJSONファイルで稼働していますが、表示される順番(ファイル単位)も保証されているようで、キーワード検索の他、顧客管理などなどあらゆるシーンで活躍できる多様性に富んだスクリプトだと確信しています。
    本当にありがとうございました。
    ajax・getjson・$.when(・concat・・・等の使い方、少し勉強してみます。
    最後になりますが、貴サイト様の益々の繁栄を祈念いたします。

  2. takizawa より:

    cly7796.net様。takizawaです。
    ご返信に深く感謝申し上げます。
    早速サンプルを拝見させていただきました。
    まさに私が希望しているものです。
    まだ試してはいませんが、取り急ぎお礼まで。
    本当にありがとうございました。
    再度ご連絡させていただきます。
    takizawa より

  3. takizawa より:

    はじめまして。
    私は、jquery等の知識がございませんので、ネット上で公開されているコードを使わせていただいてます。
    さて貴サイト様で公開されている『jQueryで実装したキーワード検索にページ送りを追加する』を拝見し、参考にさせていただきました。サイト内検索だけでなく、顧客管理等のデータベースにも最適だと思い、とても感動しました。
    そこで質問したいのですが、複数のjsonファイル(例:data1.json、data2.json・・・)を扱う場合はどのようにしたら良いのでしょうか?
    私なりに頑張ってみましたが、どうしてもうまくいきません。
    以下のコードには私が追加修正した部分(※印5箇所)です。
    —————————————————————————-
    var jsonPath = [‘data1.json’,’ data2.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’]);

    for(var i = 0; i 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(‘キーワードが入力されていません。’);
    }
    } //※追加
    });
    ——————————————————————————-
    これでも機能するのですが、問題は、キーワードに一致した記事があっても『キーワードに一致する記事がありませんでした。』のメッセージが表示されてしまいます。
    『キーワードが入力されていません。』は正常です。
    ・(‘キーワードに一致する記事がありませんでした。’)
    ・(‘取得に失敗しました。’)
    ・(‘キーワードが入力されていません。’)
    の3つのメッセージが正常に機能させるにはどうすれば良いのでしょうか?
    お忙しいかと存じますが、ご教授していただければ幸いです。

コメントを残す

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

CAPTCHA


コメントが承認されるまで時間がかかります。

2024年11月
 12
3456789
10111213141516
17181920212223
24252627282930