jQueryでキーワード検索の機能を実装する

CMSの記事情報をjsonで出力する前提で、json内の情報を検索する処理をjQueryで実装してみます。

サンプルコード

今回はこのようなjsonファイルを使って実装してみます。

{
  "data": [
    {
      "title": "約束のネバーランド",
      "body": "約束のネバーランドは、白井カイウ(原作)、出水ぽすか(作画)による日本の漫画作品。",
      "categories": ["2019", "冬", "サスペンス", "ファンタジー", "マンガ"],
      "url": "https://neverland-anime.com/"
    }, {
      "title": "私に天使が舞い降りた!",
      "body": "私に天使が舞い降りた!は、椋木ななつによる日本の漫画作品。",
      "categories": ["2019", "冬", "百合", "マンガ"],
      "url": "http://watatentv.com/"
    },
    ~ 略 ~
    {
      "title": "あそびあそばせ",
      "body": "あそびあそばせは、涼川りんによる日本の漫画。",
      "categories": ["2018", "夏", "ギャグ", "マンガ"],
      "url": "http://asobiasobase.com/"
    }
  ]
}

検索フォームは以下のようにします。

HTML

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

formのactionに検索結果ページのURLを指定してください。
今回はフォームを設置するページと検索結果ページを同じページで実装します。

次に検索結果ページに結果を出力するエリアを用意します。

HTML

<div id="js-search-result">
  <p>検索キーワードを入力してください。</p>
</div>

#js-search-result内に結果を出力する想定です。

最後に検索処理をするJavaScriptです。

JavaScript

var paramKey = 'keyword'; // 検索キーワードとして取得するパラメータのキー
var jsonPath = 'data.json'; // 記事情報のjsonのパス
var jsonKeys = ['title', 'body', 'categories']; // 検索対象にするjson内のキー
var output = "#js-search-result"; // 検索対象にするjson内のキー

$(function(){
	// URLからキーワードを取得
	var s = get_search_keywords(paramKey);

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


/**
 * 検索に使用するキーワードを取得する
 * キーワードがない場合はfalseを返す
 * @param {string} key (required) パラメータのkey
 */
function get_search_keywords(key) {
	// URLからパラメータ取得
	var params = [];
	var param = location.search.substring(1).split('&');
	for(var i = 0; i < param.length; i++) {
		params[i] = param[i].split('=');
	}
	// キーワードを配列形式で格納
	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 {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 matchIndex = [];
	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) {
				matchIndex.push(i);
			}
		}
	}
	return matchIndex;
}

/**
 * 検索に一致した記事で検索結果を生成
 * @param {object} articleData (required) 検索する記事情報
 * @param {array}  index       (required) 検索に一致する記事情報のindex
 */
function generate_result(articleData, index) {
	var data = articleData['data'];
	var ins = '';
	for (var i = 0; i < index.length; i++) {
		var t = index[i];
		ins += '<div class="article">';
			ins += '<h2 class="article_ttl">';
				ins += data[t]['title'];
			ins += '</h2>';
			ins += '<p class="article_body">';
				ins += data[t]['body'];
			ins += '</p>';
			ins += '<ul class="article_categories">';
				for(var j = 0; j < data[t]['categories'].length; j++) {
					ins += '<li>' + data[t]['categories'][j] + '</li>';
				}
			ins += '</ul>';
		ins += '</div>';
	}
	$(output).html(ins);
}

検索結果のデモページ
コード内にコメントで補足を入れているので詳細な説明は省略しますが、処理としては大きく3つに分かれていて、(1)検索に使用するキーワードを取得する、(2)記事内をキーワード検索する、(3)検索に一致した記事で検索結果を生成する、の流れになります。
 

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

関連記事

25件のコメント

  1. kenken より:

    サイト内検索のとても良い勉強になりました。
    ありがとうございます。

    知識、勉強不足で大変恐縮ですが、検索後表示されたtitleをクリックし、jsonファイルにあるurlにページ移動させる事はできますでしょうか。
    (約束のネバーランドをクリックするとhttps://neverland-anime.com/に移動など)

    宜しければご教授いただければ幸いです。

  2. Nerd より:

    掲載データに関して質問させていただきます。
    https://cly7796.net/blog/sample/implement-keyword-search-function-with-jquery/index6.html
    上記は検索ワードのハイライト表示ですが、半角英語の大文字も敗退と表示対象とするにはどのようにすればよろしいでしょうか?
    勉強不足で申し訳ございませんが、ご教授頂ければ幸いです。
    何卒宜しくお願いいたします。

  3. 宝屋 より:

    (改めてコメント投稿させていただきます)

    上記コメントのindex7の件でお世話になりました。
    1点ご報告です。

    当スクリプト(記事で公開されている一番最初のスクリプト)をヘテムル、さくらでテストしたところ問題なく動いているのですが、CPIでは動きませんでした。chrome開発者ツールのConsoleで確認したところ以下のようなエラーが出ております。

    jquery-3.3.1.min.js:2

    jQuery.Deferred exception: Cannot read properties of undefined (reading ‘length’) TypeError: Cannot read properties of undefined (reading ‘length’)
    at keyword_search (https://***.***.com/sample1/sample.js:81:27)
    at Object. (https://***.***.com/sample1/sample.js:19:17)
    at l (https://code.jquery.com/jquery-3.3.1.min.js:2:29375)
    at c (https://code.jquery.com/jquery-3.3.1.min.js:2:29677) undefined

    • cly7796.net より:

      宝屋さん

      エラー内容のみなので推測ですが、ajaxで取得したデータに問題があってエラーが出ているように見えます。

  4. takara より:

    コメント失礼します
    CMS検索で高機能に使える物はないか探していたら、
    こちらのスクリプトがかなり近いイメージなのでテストしています。
    質問ですが”categories”に設定されたワードを検索する際、
    テキストボックス内に自由記入ではなく
    チェックボックスにして選択して対応する事は可能でしょうか。

    • cly7796.net より:

      takaraさん
      コメントありがとうございます。

      サンプル作成しましたが、以下のような形でしょうか。
      https://cly7796.net/blog/sample/implement-keyword-search-function-with-jquery/index7.html

      • 宝屋 より:

        ほぼイメージにぴったりです!
        これに、最初に公開されたバージョンに入っている「テキストボックス」を組み合わせても大丈夫でしょうか。
        フリーワード入力とチェックボックス入力を用意したいと考えています。

      • 宝屋 より:

        度々すみません。
        先ほどコメントしたテキストボックスとチェックボックスを並列できないかという事については、テキストボックスにname=”categories”を付けたうえで、categoriesの中にbodyの内容を転載する事で対応できそうなので(スマートなやり方ではないかもしれませんが・・・)、先ほどの質問はカットして頂いても構いません。お手数をおかけいたします。
        他の方の改造(画像を入れる)なども入れてみて、改修を続けています。
        アドバイスを頂きありがとうございました。

  5. Sana より:

    初めまして。サイト内検索を探してこちらの記事にたどり着きました。こちらの(https://cly7796.net/blog/sample/implement-keyword-search-function-with-jquery/index4.html)
    スクリプトを使わせていただきたいのですが、以下のことはどうすれば可能でしょうか。
    ・キーワードをハイライト(span class=”highlight”で囲む)
    ・キーワードを含む4行ほどを出力
    ご助力いただけますと助かります。

    • cly7796.net より:

      Sanaさん
      コメントありがとうございます。

      ハイライトは以下のような形で問題ないでしょうか。
      https://cly7796.net/blog/sample/implement-keyword-search-function-with-jquery/index5.html

      「キーワードを含む4行ほどを出力」については結果で出力する内容にもよりますので、sample5.jsのgenerate_result()内でHTMLを適宜調整いただく形になるかと思います。

      • Sana より:

        ご返信とデモページをありがとうございます。思ったようにハイライトされ、本当に嬉しいです。

        すみません、質問が不十分だったのですが、generate_resultのをCSSで4行のみ表示させることは出来たのですが、そうするとキーワードの位置に関わらず上から4行のみ抜き出してしまい、キーワード前後4行を表示することは叶いませんでした。
        キーワード前後4行ほど(前後30字程度)抜き出すにはどうすればいいでしょうか。
        調べても思ったような結果は得られず、お力を貸していただければ大変助かります。

        どうぞよろしくお願いいたします。

  6. TOM より:

    掲載データに関して質問させていただきます。
    上記データを、自身のPC上でのみ(オフライン環境)で利用しようと思い、いろいろと試してみましたがJSONファイルのオフライン活用はできないようでした。
    データ元のJSONファイルをJSファイル等に書き直して??、上記プログラムをオフラインでも使うことができるのでしょうか?
    知識が乏しく恐縮ですが、よろしければご教授のほどよろしくお願いいたします。

  7. パルパル より:

    コメント失礼いたします
    こちらのサンプルを参考にし様々な改良を行い導入したいのですが
    検索結果の各欄に画像を表示させる事はできますか?
    (約束のネバーランドならその画像、私に天使が舞い降りたならその画像)
    画像を表示させるとしてどこをどの様に変更及び追記を行えば良いですか?

  8. Bon より:

    大変参考になりました。
    ありがとうございます。

    検索ワードに完全一致したものだけ表示させたい場合には、
    どのようにしたら良いのでしょうか?

    勉強不足で申し訳ございませんが、ご教授頂ければと思います。
    どうぞ宜しくお願いいたします。

    • cly7796.net より:

      Bonさん
      コメントありがとうございます。
      記事のサンプルだとキーワードを含んでいる記事を表示するようになっているのですが、例えばtitleとキーワードが完全一致する記事のみ表示する、みたいなことでしょうか。
      サンプルのJavaScriptを少し変更してみましたが、こういったことでしょうか。
      https://cly7796.net/blog/sample/implement-keyword-search-function-with-jquery/index2.html
      3行目で検索対象をtitleのみに変更、110行目で完全に一致するかどうかを判定するようにしています。

コメントを残す

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

CAPTCHA


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

2024年3月
 12
3456789
10111213141516
17181920212223
24252627282930
31