JavaScriptで複数選択と検索が可能なSelectboxを作成する方法

SelectBox

こんにちはフロントエンドエンジニアのまさにょんです!

今回は、JavaScriptで複数選択と検索が可能なSelectboxを作成する方法について解説していきます。

JavaScriptで複数選択と検索が可能なSelectboxを作成する

今回は、Sampleとして47都道府県の複数選択と検索が可能なSelectboxをJavaScriptで作成します。

要件定義・仕様

  1. Serverから受信した optionDataから動的に、SelectBoxを作成する
  2. 検索で、動的にSelectBoxを再作成する
  3. SelectBoxで選択したOptionは、画面に表示される
  4. 複数選択が可能である
  5. 選択したOptionは、選択状態を解除することができる

47都道府県の複数選択と検索が可能なSelectboxを作成するSampleCode全文

次の画像は、「福」で検索した後の47都道府県のSelectBoxの選択肢です。

SelectBoxで選択したものは、画面に表示されます。

SrcCodeは次のとおりです。

<style>
/* 入力フォーム */
input[type='text'] {
    width: 80%;
    height: 30px;
    margin-top: 10px;
    border-radius: 8px;
}

/* セレクトボックス */
#select-pref {
    width: 80%;
}

/* Defaultのボタンのスタイルを無効化する */
.btn {
    background-color: transparent;
    border: none;
    cursor: pointer;
    outline: none;
    padding: 0;
    appearance: none;
}

.btn {
    background: #eee;
    border-radius: 3px;
    justify-content: space-around;
    align-items: center;
    margin: 20px 10px;
    max-width: 280px;
    padding: 10px 25px;
    color: #313131;
    transition: 0.3s ease-in-out;
    font-weight: 500;
}
.btn:after {
  content: "";
  position: absolute;
  top: 50%;
  bottom: 0;
  right: 2rem;
  font-size: 90%;
  display: flex;
  justify-content: center;
  align-items: center;
  transition: right 0.3s;
  width: 6px;
  height: 6px;
  border-top: solid 2px currentColor;
  border-right: solid 2px currentColor;
  transform: translateY(-50%) rotate(45deg);
}
.btn:hover {
  background: #6bb6ff;
  color: #FFF;
}
.btn:hover:after {
  right: 1.4rem;
}

</style>
<body>
    
<h1 style="text-align: center;">日本 47都道府県</h1>
<div style="text-align: center;">
    <p>好きな都道府県を5つまで、選択可能です</p>

    <!-- Select-Box -->
    <div>
        <select name="pref" id="select-pref">
        </select>
    </div>

    <!-- 検索-Box -->
    <div style="margin-top: 12px;">
        <span>検索</span>
        <input type="text" id="searchbox">
    </div>

    <!-- 検索ボタン・リセットボタン -->
    <span>
        <input type="button" id="search" class="btn" value="検索">
        <input type="button" id="reset" class="btn" value="リセット">
    </span>

    <!-- 選択中のものが表示される -->
    <p>選択されたものが表示されます (ボタンを押すと選択が解除されます)</p>
    <div id="disp-select">
    </div>
</div>

<script>

// JavaScriptで複数選択と検索が可能なSelectboxを作成する

// [ 実行-Task-List ]

    // 1. Serverから受信した optionData から動的に、SelectBoxを作成する

    // 2. 検索で、動的にSelectBoxを再作成する

    // 3. SelectBoxで選択したOptionは、画面に表示される
    
    // 4. 複数選択が可能である

    // 5. 選択したOptionは、選択状態を解除することができる

// 選択肢・Optionの作成と追加を実行する関数
function OptionCreator (selectPrefBox, optionList, optGroupList) {
    
    optionList.forEach((opt, index) => {

    // 1. 最初は、<option value="">選択してください</option> を作成する
    if (index == 0) {
        let option = document.createElement('option');
        option.setAttribute('value', '');
        option.innerText = '選択してください';
        selectPrefBox.appendChild(option);
    }

    let option = document.createElement('option');
    option.setAttribute('value', opt.id);
    option.innerText = opt.value;

    let groupId = opt.groupId;

    let belongToGroup = document.getElementById(`${groupId}`);

    // 所属するoptgroupがなかったら、追加する
    if (belongToGroup == null) {

        const belong = optGroupList.find(group => group.id == groupId);

        selectPrefBox.appendChild(belong);

        belongToGroup = document.getElementById(`${groupId}`); // 再度、取得する
    }

    belongToGroup.appendChild(option);

    });
}

function NoMatchOption (selectPrefBox) {
    let option = document.createElement('option');
    option.setAttribute('value', '');
    option.innerText = '検索に該当はありません';
    selectPrefBox.appendChild(option);
}

// SelectBoxを作成する関数
function SelectCreator (selectPrefBox, optionList, optGroup, searchList, searchBool) {

    if (optionList.length == 0) return;

    selectPrefBox.innerHTML = ''; // 初期化処理

    // 1. グループ・カテゴリを作成する
    // optgroupタグを作成 => グループカテゴリを作成する
    const optGroupList = optGroup.map((group) => {
        let optgroup = document.createElement('optgroup');
        optgroup.setAttribute('id', group.id);
        optgroup.setAttribute('label', group.value);
        return optgroup;
    });
    
    // 2. 選択肢・Optionの作成と追加
    if (searchList.length !== 0 && searchBool) OptionCreator(selectPrefBox, searchList, optGroupList);
    else if (!searchBool) OptionCreator(selectPrefBox, optionList, optGroupList);
    else NoMatchOption(selectPrefBox);
}


// [ 1. Serverから受信した optionData から動的に SelectBoxを作成する ]

// 1-1. Serverから受信した、DataSet
const optionData = [
    {id: null, value: 'group@北海道'},
    {id: 1, value: '北海道'},
    {id: null, value: 'group@東北'},
    {id: 2, value: '青森県'},
    {id: 3, value: '岩手県'},
    {id: 4, value: '宮城県'},
    {id: 5, value: '秋田県'},
    {id: 6, value: '山形県'},
    {id: 7, value: '福島県'},
    {id: null, value: 'group@関東'},
    {id: 8, value: '茨城県'},
    {id: 9, value: '栃木県'},
    {id: 10, value: '群馬県'},
    {id: 11, value: '埼玉県'},
    {id: 12, value: '千葉県'},
    {id: 13, value: '東京都'},
    {id: 14, value: '神奈川県'},      
    {id: null, value: 'group@中部'},
    {id: 15, value: '新潟県'},
    {id: 16, value: '富山県'},
    {id: 17, value: '石川県'},
    {id: 18, value: '福井県'},
    {id: 19, value: '山梨県'},
    {id: 20, value: '長野県'},
    {id: 21, value: '岐阜県'},
    {id: 22, value: '静岡県'},
    {id: 23, value: '愛知県'},     
    {id: null, value: 'group@近畿'},
    {id: 24, value: '三重県'},
    {id: 25, value: '滋賀県'},
    {id: 26, value: '京都府'},
    {id: 27, value: '大阪府'},
    {id: 28, value: '兵庫県'},
    {id: 29, value: '奈良県'},
    {id: 30, value: '和歌山県'},
    {id: null, value: 'group@中国'},
    {id: 31, value: '鳥取県'},
    {id: 32, value: '島根県'},
    {id: 33, value: '岡山県'},
    {id: 34, value: '広島県'},
    {id: 35, value: '山口県'},
    {id: null, value: 'group@四国'},
    {id: 36, value: '徳島県'},
    {id: 37, value: '香川県'},
    {id: 38, value: '愛媛県'},
    {id: 39, value: '高知県'},      
    {id: null, value: 'group@九州沖縄'},
    {id: 40, value: '福岡県'},
    {id: 41, value: '佐賀県'},
    {id: 42, value: '長崎県'},
    {id: 43, value: '熊本県'},
    {id: 44, value: '大分県'},
    {id: 45, value: '宮崎県'},
    {id: 46, value: '鹿児島県'},
    {id: 47, value: '沖縄県'},
];


// 1-2. Group-配列 => {id: 'group_1', value: '北海道'}[]
const optGroup = [];

let groupCount = 1;
let groupId = '';

// 1-3. 47都道府県データに、groupを紐づける
// {id: 1, value: '北海道', groupId: 'group_1'}[]
const optionCustom = optionData.filter((opt, index) => {

    // グループ・データなら、加工して、optGroupに投入する
    if (/^group@/.test(opt.value)) {
        groupId = `group_${groupCount}`;
        groupCount++;
        opt.id = groupId;
        opt.value = opt.value.replace("group@", "");
        optGroup.push(opt);
    } else {
        opt.groupId = groupId;
        return opt;
    }
});

console.log({optionCustom});

console.log({optGroup});

// 1-4. SelectBoxを取得する
let selectPrefBox = document.getElementById('select-pref');

// 1-5. 初期のSelectBoxを作成する
SelectCreator(selectPrefBox, optionCustom, optGroup, [], false);


// [ 2. 検索で、動的にSelectBoxを再作成する  ]

// 2-1. 検索のための入力フォームを取得する
const searchbox = document.getElementById('searchbox');

// 2-2. 検索結果のOption-List
let searchResult = [];

// 2-3. inputイベントで検索の入力文字列を受け取って、検索結果の配列を作成する
searchbox.addEventListener('input', (e) => {

    let searchStr = e.target.value; // 検索文字列

    searchResult = optionCustom.filter((pref) => { // 検索文字列から絞り込む
        if (/^group@/.test(pref.value)) return false;

        // 正規表現で変数を使用するためには、RegExp-Classを使用する
        let reg = new RegExp(`${searchStr}`); // 部分一致

        // 検索文字列のパターンと、登録データがマッチするかでTestをする
        return reg.test(pref.value);
    });
    console.log({searchResult});
});

// 2-4. 検索ボタン
let searchBtn = document.getElementById('search');

// 2-5. 検索ボタンに、clickイベントを追加する
// searchResultで SelectBoxを作成する
search.onclick = () => SelectCreator(selectPrefBox, optionCustom, optGroup, searchResult, true);

// [ 3. SelectBoxで選択したものは、画面に表示される => 複数選択が可能である ]

// 3-1. 選択中のものを表示するInput-Boxを取得する
let dispDiv = document.getElementById('disp-select');

// 3-2. SelectBoxに changeイベントを追加する
selectPrefBox.addEventListener('change', (e) => {

    // 選択してくださいは、弾く!
    if (!e.target.value) return;

    // 1. 上限を5つまでにして、それ以上は、弾く!
    if (6 <= dispDiv.childElementCount + 1) {
        alert('選択できる数は、5つまでです');
        return;
    }

    // 2. valueにidを付与している
    const id = Number(e.target.value);

    // 3. 選択済みの都道府県は、弾く!
    const idList = [...dispDiv.children].map(btn => btn.id);

    console.log({idList});

    if (idList.some(i => Number(i) == id)) {
        alert('選択済みです');
        return;
    }

    // 4. 該当の optionデータを取得する
    const option = optionCustom.find(opt => opt.id === id);
    console.log(option);

    // 5. input-btn を作成して、optionのデータを紐付ける
    let input = document.createElement('input');
    input.setAttribute('type', 'button');
    input.setAttribute('id', option.id);
    input.setAttribute('value', option.value);
    input.classList.add('btn');

    // 6. 選択を解除する機能を作成した input-btn に付与する
    input.onclick = e => dispDiv.removeChild(e.target);

    // 7. 選択されたoptionを input-btnとして画面に表示する
    dispDiv.appendChild(input);

});

// [ 4. 検索状態をResetする機能を作成する ]

// 4-1. 検索・リセットボタンを取得する
let resetBtn = document.getElementById('reset');

// 4-2. 検索・リセットボタンに、clickイベントを追加する
// SelectBoxの optionをリセットする
resetBtn.onclick = () => SelectCreator(selectPrefBox, optionCustom, optGroup, [], false);

</script>

</body>

JavaScript書籍 Ver. 中級-上級者向け

JavaScript書籍 Ver. 初級者向け

Twitterやってます!Follow Me!

神聖グンマー帝国の逆襲🔥

神聖グンマー帝国の科学は、世界一ぃぃぃぃぃぃ!!!!!

最近の投稿