<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>アイテムリスト(Choices.js & Flatpickr)</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<style>
/* --- CSS スタイル (Flatpickr, Choices.js 用の調整は不要であればそのまま) --- */
.c-datalist-detail {
display: none;
margin-left: 20px;
border: 1px solid #ddd;
padding: 10px;
margin-top: 8px;
background-color: #f9f9f9;
border-radius: 4px;
}
.c-datalist-item {
border: 1px solid #e0e0e0;
padding: 15px;
margin-bottom: 15px;
background-color: #fff;
border-radius: 6px;
}
.addClone {
cursor: pointer;
display: inline-block;
padding: 8px 15px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
transition: background-color 0.3s;
font-weight: bold;
}
.addClone:hover {
background-color: #0056b3;
}
/* Choices.js が生成するラッパー要素にマージンを持たせる */
.c-datalist-item .choices {
display: inline-flex;
vertical-align: middle;
margin-right: 15px;
}
/* チェックボックスの配置調整 */
.c-datalist-item > label {
display: inline-block;
vertical-align: middle;
}
</style>
</head>
<body>
<div class="c-wrapper">
<div class="c-datalist">
<div class="c-datalist-item" id="original-item">
<select name="item-select[]" class="item-select">
<option value="1" data-detail="type1">アイテム1 (詳細あり)</option>
<option value="2" data-detail="type2">アイテム2 (詳細あり)</option>
<option value="3" data-detail="type3">アイテム3 (詳細あり)</option>
<option value="4">アイテム4 (詳細なし)</option>
</select>
<label>
<input type="checkbox" class="detail-checkbox" />
**詳しく探す**
</label>
<div class="c-datalist-detail" data-detail="type1">
Type1 の詳細入力: <input type="text" value="デフォルト値A"><br>
日付の入力: <input type="text" class="date-input" placeholder="日付を選択 (type1)">
</div>
<div class="c-datalist-detail" data-detail="type2">
Type2 の詳細入力: <input type="text" value="デフォルト値B"><br>
日付の入力: <input type="text" class="date-input" placeholder="日付を選択 (type2)">
</div>
<div class="c-datalist-detail" data-detail="type3">
Type3 の詳細入力: <input type="text" value="デフォルト値C"><br>
日付の入力: <input type="text" class="date-input" placeholder="日付を選択 (type3)">
</div>
</div>
<div class="addClone">増やす</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr/dist/l10n/ja.js"></script>
<script>
// --- JavaScript ロジック ---
document.addEventListener('DOMContentLoaded', () => {
const dataListContainer = document.querySelector('.c-datalist');
const addCloneButton = document.querySelector('.addClone');
const originalItem = document.getElementById('original-item');
// Flatpickr のデフォルト設定 (日本語化)
const flatpickrConfig = {
locale: flatpickr.l10ns.ja,
allowInput: true, // 手入力も許可
};
// 詳細表示/非表示を切り替えるメイン関数(変更なし)
const toggleDetail = (item) => {
const checkbox = item.querySelector('.detail-checkbox');
const select = item.querySelector('.item-select');
const allDetails = item.querySelectorAll('.c-datalist-detail');
const isChecked = checkbox.checked;
// Choices.js を使用している場合、select.options から値を取得
let selectedOption;
if (select.choicesjs) {
// Choices.js のインスタンスから選択された要素を取得
const activeItems = select.choicesjs.getValue(true);
// Choices.js は配列を返すため、単一選択の場合は最初の要素を取得
const selectedValue = Array.isArray(activeItems) ? activeItems[0] : activeItems;
// 選択された value に対応する option 要素を探す
selectedOption = Array.from(select.options).find(opt => opt.value === selectedValue);
} else {
selectedOption = select.options[select.selectedIndex];
}
const selectedDetailType = selectedOption ? selectedOption.dataset.detail : null;
allDetails.forEach(detail => {
detail.style.display = 'none';
});
if (isChecked && selectedDetailType) {
const targetDetail = item.querySelector(`.c-datalist-detail[data-detail="${selectedDetailType}"]`);
if (targetDetail) {
targetDetail.style.display = 'block';
}
}
};
// アイテムにイベントリスナーと外部ライブラリを設定する関数
const setupItemListeners = (item) => {
const checkbox = item.querySelector('.detail-checkbox');
const select = item.querySelector('.item-select');
const dateInputs = item.querySelectorAll('.date-input');
// --- 1. Choices.js の初期化 ---
// 既存のインスタンスを破棄する (複製後の要素用)
if (select.choicesjs) {
select.choicesjs.destroy();
}
const choices = new Choices(select, {
searchEnabled: false, // 検索機能を無効化 (任意)
itemSelectText: '',
});
// インスタンスを要素に保持させておく
select.choicesjs = choices;
// --- 2. Flatpickr の初期化 ---
dateInputs.forEach(input => {
// 既存の Flatpickr インスタンスを破棄(複製後の要素用)
if (input._flatpickr) {
input._flatpickr.destroy();
}
flatpickr(input, flatpickrConfig);
});
// --- 3. 詳細表示ロジックの設定 ---
// 初期状態を設定
toggleDetail(item);
// イベントリスナーを設定
checkbox.addEventListener('change', () => toggleDetail(item));
// Choices.js のカスタムイベントを使用
select.addEventListener('change', () => toggleDetail(item));
};
// ページロード時に最初のアイテムにリスナーを設定
if (originalItem) {
setupItemListeners(originalItem);
}
// 「増やす」ボタンのクリックイベント
addCloneButton.addEventListener('click', () => {
const sourceItem = document.getElementById('original-item');
if (!sourceItem) {
console.error("複製元の要素 (id='original-item') が見つかりません。");
return;
}
// 【重要】複製前に Choices.js のラッパー要素を削除し、元の select 要素に戻す
const selectToClone = sourceItem.querySelector('.item-select');
if (selectToClone && selectToClone.choicesjs) {
// Choices.js が生成したラッパー要素 (div.choices) を削除
const choicesWrapper = selectToClone.closest('.choices');
if (choicesWrapper) {
choicesWrapper.remove();
}
// 元の SELECT 要素を sourceItem に戻す(cloneNodeで複製されるのは元の要素のみ)
sourceItem.prepend(selectToClone);
// Choices.js インスタンスを破棄し、要素から参照を削除
selectToClone.choicesjs.destroy();
selectToClone.choicesjs = null;
}
// 要素を複製
const newItem = sourceItem.cloneNode(true);
// 複製された要素内の入力値やチェック状態をリセット
const newSelect = newItem.querySelector('.item-select');
const newCheckbox = newItem.querySelector('.detail-checkbox');
const newDateInputs = newItem.querySelectorAll('.date-input');
newSelect.selectedIndex = 0;
newCheckbox.checked = false;
// 日付入力フィールドの値をクリア
newDateInputs.forEach(input => {
input.value = '';
});
// 複製した要素にイベントリスナーとライブラリを設定
setupItemListeners(newItem);
// 「増やす」ボタンの直前に追加
dataListContainer.insertBefore(newItem, addCloneButton);
// 【重要】複製後、元の sourceItem も再初期化して Choices.js を適用し直す
setupItemListeners(sourceItem);
});
});
</script>
</body>
</html>
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme