JavaScriptで複数ドロップダウンの連動絞り込み方法について

以下のトピックを参考にして、大中小の連動するドロップボックスを作成できたのですが、何故か、大を変更すると中は正常なものが選択されますが、小が中の最後の要素が表示されしまいます。

以下のような優先順のデータがあるとしたら、Aを選択するとBが表示されてCが表示するはずが、Gが表示されてしまいます。
A-B-C
AーD-E
A-F-G

JavaScriptが素人という事もありますが、どうしても読み解けませんでした。

視点をアドバイス頂けますと幸いです。

// カテゴリーマスタ・アプリの情報
var MASTER_CATEGORY_APP_NO = 74;
var MASTER_CATEGORY_QUERY = ' 有効フラグ in ("ON") ';
var MASTER_BIG_CATEGORY_NAME    = 'マスタ大';
var MASTER_MIDDLE_CATEGORY_NAME = 'マスタ中';
var MASTER_SMALL_CATEGORY_NAME  = 'マスタ小';

// プルダウンの値を格納するフィールド名(このJSを読み込んでいるアプリ)
var BIG_CATEGORY_NAME    = '大';
var MIDDLE_CATEGORY_NAME = '中';
var SMALL_CATEGORY_NAME  = '小';


(function () {

	//別kintoneアプリからの取得クラス変数
	var getRecordMethod;


	// 新規レコード追加・編集画面になったら
	kintone.events.on(['app.record.create.show','app.record.edit.show'], function (event) {

		// 元々ある大中小のテキスト入力フィールドは非表示にする(表示・操作はプルダウンだけにしたい)
		kintone.app.record.setFieldShown(BIG_CATEGORY_NAME, false);
		kintone.app.record.setFieldShown(MIDDLE_CATEGORY_NAME, false);
		kintone.app.record.setFieldShown(SMALL_CATEGORY_NAME, false);

		// 既存レコードの場合、選択済のカテゴリーの値を、変数に格納する。
		var big_cat_selected = event["record"][BIG_CATEGORY_NAME]["value"];
		var middle_cat_selected = event["record"][MIDDLE_CATEGORY_NAME]["value"];
		var small_cat_selected = event["record"][SMALL_CATEGORY_NAME]["value"];
		
		// 大中小のプルダウンを表示するエリアを生成
		var mySpaceField = kintone.app.record.getSpaceElement('my_space_field');

		// 別アプリである「カテゴリーマスタ」のデータを入れる連想配列を初期化
		var hashItems = new Array();

		//別kintoneアプリからの取得クラスを生成(リロードや二回目の編集を考慮して初期化)
		getRecordMethod = new KintoneRecordManager();		
		getRecordMethod.records = [];
		getRecordMethod.offset = 0;


		//全てのレコードセットをグローバル変数に格納(WebAPI経由なので1秒位かかる)
		getRecordMethod.getRecords(function(records) {

			// 0件なら何もしない
			if (records.length == 0) {
				mySpaceField.innerHTML = 'マスタアプリにレコードがありません';
				return;
			}

			// マスタアプリのデータを、ローカルの連想配列に格納
			for (var i = 0; i < records.length; i++) {
				var record = records[i];
				hashItems[i] = {
					big_cat: record[MASTER_BIG_CATEGORY_NAME]["value"], 
					middle_cat: record[MASTER_MIDDLE_CATEGORY_NAME]["value"], 
					small_cat: record[MASTER_SMALL_CATEGORY_NAME]["value"] 
				};
			}

			// 新規作成なら(選択済が無ければ)、大・中・小カテゴリーの最初の選択肢を選択済にしておく
			if(big_cat_selected == undefined) { big_cat_selected = hashItems[0]['big_cat'];}
			if(middle_cat_selected == undefined) { middle_cat_selected = hashItems[0]['middle_cat'];}
			if(small_cat_selected == undefined) { small_cat_selected = hashItems[0]['small_cat'];}
			

			//最初にプルダウンを、全てクリアする
			for (var i=mySpaceField.childNodes.length-1; i>=0; i--) {
				mySpaceField.removeChild(mySpaceField.childNodes[i]);
			}

			//大・中・小カテゴリーのプルダウンを作成					
			var select1 = document.createElement("select"); select1.name = "big_cat";
			var select2 = document.createElement("select"); select2.name = "middle_cat";
			var select3 = document.createElement("select"); select3.name = "small_cat";

			//プルダウンオブジェクト・選択済項目・親カテゴリ・自カテゴリの定義配列
			var arrCategory = new Array(
				{"pulldown":select1, "selected":big_cat_selected,    "parent":'',           "self":'big_cat'},
				{"pulldown":select2, "selected":middle_cat_selected, "parent":'big_cat',    "self":'middle_cat'},
				{"pulldown":select3, "selected":small_cat_selected,  "parent":'middle_cat', "self":'small_cat'}
			);

			//大・中・小カテゴリーのプルダウン作成
			for(iCat=0; iCat <arrCategory.length; iCat++){
				//カテゴリーマスタのレコード数だけループ(重複したフィールド値を1個にまとめる)
				var first_value = "";
				for ( var i in hashItems ) {
					// 親カテゴリーが無い or 親カテゴリーが一致なら
					if(arrCategory[iCat]['parent'] == '' || arrCategory[iCat-1]['selected'] == hashItems[i][arrCategory[iCat]['parent']]){
						// かつ、自カテゴリの新しい値だったら、プルダウンに追加する
						if(first_value !=  hashItems[i][arrCategory[iCat]['self']]){
							var option = document.createElement('option');
							option.setAttribute('value', hashItems[i][arrCategory[iCat]['self']]);
							//既存レコードの選択済の値だったら、selectedにしておく
							if(arrCategory[iCat]['selected'] == hashItems[i][arrCategory[iCat]['self']]){
								option.setAttribute('selected', true);
							}
							option.innerHTML = hashItems[i][arrCategory[iCat]['self']];
							arrCategory[iCat]['pulldown'].appendChild(option);
							// プルダウンに追加した値を、次の比較対象とする
							first_value = hashItems[i][arrCategory[iCat]['self']];
						}
					}
				}
			}


			// 大カテゴリーを変更
			select1.onchange = function(event) {  
				
				var first_value_middle = "";
				var first_value_small = "";

				// 中カテゴリーをクリア
				for (var i=select2.childNodes.length-1; i>=0; i--) {
					select2.removeChild(select2.childNodes[i]);
				}
				//カテゴリーマスタのレコード数だけループ
				for ( var i in hashItems ) {
					//新しい中カテゴリーが出てきたらプルダウンに追加する
					if(this.value == hashItems[i]['big_cat'] && first_value_middle !=  hashItems[i]['middle_cat']){
						var option2 = document.createElement('option');
						option2.setAttribute('value', hashItems[i]['middle_cat']);
						option2.innerHTML = hashItems[i]['middle_cat'];

						select2.appendChild(option2);

						first_value_middle = hashItems[i]['middle_cat'];

						//// 中カテゴリーが変更されたら、小カテゴリーの項目も変更する
						for (var i=select3.childNodes.length-1; i>=0; i--) {
							select3.removeChild(select3.childNodes[i]);
						}				
						//カテゴリーマスタのレコード数だけループ
						for ( var i in hashItems ) {
							//新しい中カテゴリーが出てきたら、プルダウンに追加する
							if(first_value_middle == hashItems[i]['middle_cat'] && first_value_small !=  hashItems[i]['small_cat']){
								var option3 = document.createElement('option');
								option3.setAttribute('value', hashItems[i]['small_cat']);
								option3.innerHTML = hashItems[i]['small_cat'];
								select3.appendChild(option3);
								// プルダウンに追加した値を、次の比較対象とする
								first_value_small = hashItems[i]['small_cat'];
							}
						}
					}
				}

			}

			// 中カテゴリーを変更  
			select2.onchange = function(event) {  

				var first_value = "";

				for (var i=select3.childNodes.length-1; i>=0; i--) {
					select3.removeChild(select3.childNodes[i]);
				}
				//カテゴリーマスタのレコード数だけループ
				for ( var i in hashItems ) {
					//新しい小カテゴリーが出てきたら、プルダウンに追加する
					if(this.value == hashItems[i]['middle_cat'] && first_value !=  hashItems[i]['small_cat']){
						var option3 = document.createElement('option');
						option3.setAttribute('value', hashItems[i]['small_cat']);
						option3.innerHTML = hashItems[i]['small_cat'];
						select3.appendChild(option3);
						// プルダウンに追加した値を、次の比較対象とする
						first_value = hashItems[i]['small_cat'];
					}
				}
			}
			
			// 小カテゴリーの変更は、他に影響が無いので特に処理は行わない。
			
			
			// 画面に、大・中・小カテゴリーのプルダウンを表示する
			mySpaceField.appendChild(select1);
			mySpaceField.appendChild(select2);
			mySpaceField.appendChild(select3);

			// CSSを使って、見た目をkintoneっぽくする
			//プルダウンオブジェクト・項目名の定義配列
			var arrOutput = new Array(
				{"pulldown":select1, "fieldname":BIG_CATEGORY_NAME},
				{"pulldown":select2, "fieldname":MIDDLE_CATEGORY_NAME},
				{"pulldown":select3, "fieldname":SMALL_CATEGORY_NAME}
			);
			
			//親子関係カテゴリーのプルダウンを画面に表示する
			for(iOutput=0; iOutput <arrOutput.length; iOutput++){
				
				// 見た目がkintoeデフォルトと同じようになるように、CSS解析してセット(崩れる可能性あり)
				var div_outer = document.createElement('div');
				div_outer.className = 'control-gaia control-single-select-field-gaia';
				
				var span = document.createElement('span');
				span.innerHTML = arrOutput[iOutput]['fieldname'];
				span.className = 'control-label-text-gaia';
				var div = document.createElement('div');
				div.className = 'control-label-gaia';
				div.appendChild(span);
				div_outer.appendChild(div);
			
				arrOutput[iOutput]['pulldown'].style.width = '230px';
				arrOutput[iOutput]['pulldown'].style.cursor = "pointer";
				

				div_outer.appendChild(arrOutput[iOutput]['pulldown']);

				mySpaceField.appendChild(div_outer);

			}
		});	
		return event;


	});

	// レコード追加・編集保存前イベント
	kintone.events.on(['app.record.create.submit', 'app.record.edit.submit'], function(event) {

		// プルダウンで選択されている値を、kintoneレコードに格納する
		event["record"][BIG_CATEGORY_NAME]["value"] = $('select[name="big_cat"]').val();
		event["record"][MIDDLE_CATEGORY_NAME]["value"] = $('select[name="middle_cat"]').val();
		event["record"][SMALL_CATEGORY_NAME]["value"] = $('select[name="small_cat"]').val();
		return event;
	});

})();


 /**
* Kintoneと通信を行うクラス()
*/
KintoneRecordManager = (function() {
    KintoneRecordManager.prototype.query = '';

    KintoneRecordManager.prototype.records = [];

    KintoneRecordManager.prototype.appId = null;

    KintoneRecordManager.prototype.query = MASTER_CATEGORY_QUERY;

    KintoneRecordManager.prototype.limit = 100;

    KintoneRecordManager.prototype.offset = 0;

    function KintoneRecordManager() {
//      this.appId = kintone.app.getId();
        this.appId = MASTER_CATEGORY_APP_NO;
    }

    // すべてのレコード取得する
    KintoneRecordManager.prototype.getRecords = function(callback) {
        kintone.api('/k/v1/records', 'GET', {
            app: this.appId,
            query: this.query + (' order by 優先度 asc limit ' + this.limit + ' offset ' + this.offset ) //order by,limit, offsetの順番じゃないとダメ!
        }, (function(_this) {
            return function(res) {
                var len=0;
                Array.prototype.push.apply(_this.records, res.records);
                len = res.records.length;
                _this.offset += len;
                if (len < _this.limit) {
                    _this.ready = true;
                    if (callback !== null) {
                        callback(_this.records);
                    }
                } else {
                    _this.getRecords(callback);
                }
            };
        })(this));
    };

    return KintoneRecordManager;
})();```

大を変更したときに想定通り動かない、という理解でいいでしょうか?(大を変更したとき中・小ともに変更がはしる)

であれば、まず第一歩として、

  	// 大カテゴリーを変更
  	select1.onchange = function(event) {  

の部分からデバッグしてみるのはどうでしょう。
なぜ本来はいってほしくない値がはいるのか、 console にだしたり、ブレークポイントを貼って値がどうなっているのかを覗く、ということからスタートされるといいとおもいます。
https://cybozu.dev/ja/id/42b1d7ce01fd5d9b53dbf4df/#methods-list-4

ありがとうございます。ちょっと、デバックが不得意な部分もありますが、
この機会に、しっかり勉強しておこうと思います。

新規追加ボタン押下・編集ボタン押下・キャンセルボタン押下時には、ご指摘のブレークポイントに止まりますが、その後ドロップダウンを変更しても、ブレークポイントには止まらず、そもそもロジックにさえ入らないようです。しかし、値はおかしい(大を変更すると、中は正しい値が表示されるが、小は中の最下段が選択された時の値が表示される)ものの値は変更されるようです。デバックの仕方がおかしいのでしょうか?

onclickの中の行に設定すると止まるはずですけどね、色々設置して試してはどうでしょうか、
小がおかしい、ということであれば下記付近がそれにあたるのかな、とおもうので、ログだすか、ブレークポイントでみていくのは必要にみえますかね

							//新しい中カテゴリーが出てきたら、プルダウンに追加する
							if(first_value_middle == hashItems[i]['middle_cat'] && first_value_small !=  hashItems[i]['small_cat']){
								var option3 = document.createElement('option');
								option3.setAttribute('value', hashItems[i]['small_cat']);
								option3.innerHTML = hashItems[i]['small_cat'];
								select3.appendChild(option3);
								// プルダウンに追加した値を、次の比較対象とする
								first_value_small = hashItems[i]['small_cat'];
							}

度々のご返信ありがとうございます。
やはり、そうですよね。あちこちにポイント置いてみたのですが、レコード追加と編集ボタン押下時しか止まらなかったので、何でなんだろうと・・・
もう少し、ポイントを変えて確認してみます。

やはり、レコード追加と編集時にしか止まりませんでした。よくわからずに、見よう見まねでやりましたが、Kintone UI Compornentを使っているみたいなので、事前準備をやったら、後はちゃんと動く(Kintoneのコンポーネントとして)ようですね。稼働重視で、市販プラグインも視野に入れながら、調査・検討します。

1 Like