返品・交換リクエストフォーム
Shopify用の返品・交換リクエストフォームデモページです。注文番号・注文日・商品名の入力、返品(返金)または交換の選択、理由の分類選択(商品破損・欠陥・誤配送・サイズ違い・イメージ違い等)に対応。商品状態の写真はドラッグ&ドロップで複数添付でき、サムネイルでプレビュー表示されます。ポリシー同意のチェックボックスも設置済み。カスタマーサポート業務を効率化し、返品・交換対応の品質向上に貢献します。HTML・CSS・JavaScriptをコピーしてすぐにご利用いただけます。
プレビュー
※プレビューのため送信ボタンは押せません
コード
HTML
<h1>返品・交換リクエスト</h1>
<p class="form-description">商品の返品・交換をご希望の場合は、以下のフォームよりお申し込みください。</p>
<div class="note">
※ 商品到着後7日以内にお申し込みください。<br>
※ お客様都合の返品は未使用・未開封の商品に限ります。
</div>
<form action="#" method="POST" enctype="multipart/form-data">
<div class="form-row">
<div class="form-group">
<label for="name" class="required">お名前</label>
<input type="text" id="name" name="name" required autocomplete="name">
</div>
<div class="form-group">
<label for="email" class="required">メールアドレス</label>
<input type="email" id="email" name="email" required autocomplete="email">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="order_number" class="required">注文番号</label>
<input type="text" id="order_number" name="order_number" placeholder="例: #12345" required>
</div>
<div class="form-group">
<label for="order_date">ご注文日</label>
<input type="date" id="order_date" name="order_date">
</div>
</div>
<div class="form-group">
<label for="product_name" class="required">商品名</label>
<input type="text" id="product_name" name="product_name" placeholder="返品・交換をご希望の商品名" required>
</div>
<div class="form-group">
<label class="required">ご希望の対応</label>
<div class="radio-group">
<label class="radio-item">
<input type="radio" name="request_type" value="return" required>
<span>返品(返金)</span>
</label>
<label class="radio-item">
<input type="radio" name="request_type" value="exchange">
<span>交換</span>
</label>
</div>
</div>
<div class="form-group">
<label for="reason" class="required">理由</label>
<select id="reason" name="reason" required>
<option value="">選択してください</option>
<optgroup label="商品の不具合">
<option value="damaged">商品が破損していた</option>
<option value="defective">商品に欠陥がある</option>
<option value="wrong_item">注文と異なる商品が届いた</option>
</optgroup>
<optgroup label="お客様都合">
<option value="size">サイズが合わない</option>
<option value="color">色・イメージが違った</option>
<option value="duplicate">重複して注文した</option>
<option value="other_customer">その他</option>
</optgroup>
</select>
</div>
<div class="form-group">
<label for="detail" class="required">詳細</label>
<textarea id="detail" name="detail" placeholder="具体的な状況をご記入ください(破損箇所、交換希望のサイズ・色など)" required></textarea>
</div>
<div class="form-group">
<label>商品の状態がわかる写真</label>
<div class="drop-zone">
<input type="file" name="images[]" accept="image/*" multiple class="file-input">
<div class="drop-zone-text">画像をドラッグ&ドロップ、またはクリックして選択</div>
<div class="drop-zone-hint">複数選択可・1ファイル最大10MB(jpg, png, webp)</div>
</div>
<ul class="previews"></ul>
</div>
<div class="form-group">
<label class="checkbox-item">
<input type="checkbox" name="agree" required>
<span>返品・交換ポリシーに同意します</span>
</label>
</div>
<button type="submit" class="btn-submit">送信する</button>
</form>
CSS
.form-description {
color: #666;
margin-bottom: 32px;
font-size: 14px;
}
.form-group {
margin-bottom: 24px;
}
label {
display: block;
font-weight: 500;
margin-bottom: 8px;
font-size: 14px;
}
.required::after {
content: "*";
color: #e53935;
margin-left: 4px;
}
input[type="text"],
input[type="email"],
input[type="tel"],
input[type="date"],
select,
textarea {
width: 100%;
padding: 12px 16px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 16px;
transition: border-color 0.2s, box-shadow 0.2s;
box-sizing: border-box;
}
input:focus,
select:focus,
textarea:focus {
outline: none;
border-color: #2563eb;
box-shadow: 0 0 0 3px rgba(37,99,235,0.1);
}
textarea {
min-height: 120px;
resize: vertical;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
@media (max-width: 540px) {
.form-row {
grid-template-columns: 1fr;
}
}
.radio-group {
display: flex;
gap: 24px;
flex-wrap: wrap;
}
.radio-item {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
}
.radio-item input[type="radio"] {
width: 18px;
height: 18px;
accent-color: #2563eb;
}
.checkbox-item {
display: flex;
align-items: flex-start;
gap: 8px;
cursor: pointer;
}
.checkbox-item input[type="checkbox"] {
width: 18px;
height: 18px;
margin-top: 2px;
accent-color: #2563eb;
}
/* ドロップゾーン(画像添付) */
.drop-zone {
border: 2px dashed #ddd;
border-radius: 8px;
padding: 32px;
text-align: center;
cursor: pointer;
transition: border-color 0.2s, background 0.2s;
position: relative;
}
.drop-zone:hover,
.drop-zone.is-dragover {
border-color: #2563eb;
background: #f8fafc;
}
.drop-zone .file-input {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
}
.drop-zone-text {
color: #666;
font-size: 14px;
}
.drop-zone-hint {
color: #999;
font-size: 12px;
margin-top: 8px;
}
/* プレビューリスト */
.previews {
list-style: none;
padding: 0;
margin: 12px 0 0;
}
.previews .preview {
padding: 8px 12px;
background: #f5f5f5;
border-radius: 4px;
font-size: 13px;
color: #333;
margin-bottom: 6px;
}
.btn-submit {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 14px 32px;
background: #2563eb;
color: #fff;
border: none;
border-radius: 6px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: background 0.2s, transform 0.1s;
}
.btn-submit:hover {
background: #1d4ed8;
}
.btn-submit:active {
transform: translateY(1px);
}
.note {
background: #fef3c7;
border-left: 4px solid #f59e0b;
padding: 12px 16px;
margin-bottom: 24px;
font-size: 14px;
color: #92400e;
}
JavaScript
const form = document.querySelector("form");
const fileInput = form.querySelector(".file-input");
const dropZone = form.querySelector(".drop-zone");
const previewList = form.querySelector(".previews");
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
let selectedFiles = [];
function addFiles(list){
for(const file of list){
if(!file.type.startsWith("image/")) { console.warn("画像以外は不可:", file.name); continue; }
if(file.size > MAX_FILE_SIZE) { console.warn("10MB超過:", file.name); continue; }
selectedFiles.push(file);
}
const dt = new DataTransfer();
selectedFiles.forEach(f => dt.items.add(f));
fileInput.files = dt.files;
previewList.innerHTML = "";
selectedFiles.forEach(f => {
const li = document.createElement("li");
li.className = "preview";
li.textContent = `${f.name} (${(f.size/1024/1024).toFixed(1)} MB)`;
previewList.appendChild(li);
});
}
// 誤ドロップ遷移防止
addEventListener("dragover", e => e.preventDefault());
addEventListener("drop", e => e.preventDefault());
// D&D
["dragenter","dragover"].forEach(ev => {
dropZone.addEventListener(ev, e => { e.preventDefault(); dropZone.classList.add("is-dragover"); });
});
["dragleave","drop"].forEach(ev => {
dropZone.addEventListener(ev, e => { e.preventDefault(); dropZone.classList.remove("is-dragover"); });
});
dropZone.addEventListener("drop", e => addFiles(e.dataTransfer.files));
fileInput.addEventListener("change", e => addFiles(e.target.files));