用戶列表頁,每行尾部「修改」按鈕,點擊彈出模態框,自動填充該行數據 (用戶名、昵稱、年齡),修改後提交保存。
<c:forEach var="item" items="${list}" varStatus="status">
<tr>
<td>${item.name}</td>
<td>${item.niceName}</td>
<td>${item.age}</td>
<td>
<a href="#" class="edit-btn"
data-id="${item.id}"
data-name="${item.name}"
data-nickname="${item.niceName}"
data-age="${item.age}">修改</a>
</td>
</tr>
</c:forEach>
**關鍵**:用 data-* 屬性攜帶行數據,避免 DOM 遍歷 parentNode 易斷裂。
<div id="editModal" class="modal" style="display:none;">
<div class="modal-content">
<h3>編輯用戶</h3>
<form id="editForm">
<input type="hidden" name="id" id="editId">
<div><label>用戶名:</label><input name="name" id="editName"></div>
<div><label>昵稱:</label><input name="nickname" id="editNickname"></div>
<div><label>年齡:</label><input name="age" id="editAge" type="number"></div>
<button type="submit">保存</button>
<button type="button" id="closeModal">關閉</button>
</form>
</div>
</div>
<div id="modalOverlay" class="overlay" style="display:none;"></div>
document.addEventListener('DOMContentLoaded', () => {
const modal = document.getElementById('editModal');
const overlay = document.getElementById('modalOverlay');
const form = document.getElementById('editForm');
const closeBtn = document.getElementById('closeModal');
// 事件代理:列表動態生成也生效
document.querySelector('table').addEventListener('click', e => {
const btn = e.target.closest('.edit-btn');
if (!btn) return;
e.preventDefault();
// 填充數據
document.getElementById('editId').value = btn.dataset.id;
document.getElementById('editName').value = btn.dataset.name;
document.getElementById('editNickname').value = btn.dataset.nickname;
document.getElementById('editAge').value = btn.dataset.age;
// 顯示彈層
modal.style.display = 'block';
overlay.style.display = 'block';
});
// 關閉彈層
const hide = () => { modal.style.display = 'none'; overlay.style.display = 'none'; };
closeBtn.addEventListener('click', hide);
overlay.addEventListener('click', hide);
// 提交表單 (AJAX)
form.addEventListener('submit', async e => {
e.preventDefault();
const formData = new FormData(form);
try {
const resp = await fetch('update', {
method: 'POST',
body: new URLSearchParams(formData)
});
if (resp.ok) {
alert('修改成功');
hide();
location.reload(); // 簡單刷新列表
} else {
alert('修改失敗: ' + await resp.text());
}
} catch (err) {
alert('網絡錯誤: ' + err.message);
}
});
});
.modal {
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: white; padding: 20px; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,.2);
z-index: 1001; min-width: 300px;
}
.overlay {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,.5); z-index: 1000;
}
.modal-content div { margin-bottom: 12px; }
.modal-content label { display: block; margin-bottom: 4px; font-weight: bold; }
.modal-content input { width: 100%; padding: 8px; box-sizing: border-box; }
@PostMapping("/update")
@ResponseBody
public Result updateUser(@RequestParam Integer id,
@RequestParam String name,
@RequestParam String nickname,
@RequestParam Integer age) {
User user = new User();
user.setId(id);
user.setName(name);
user.setNiceName(nickname);
user.setAge(age);
userService.update(user);
return Result.success();
}
| 傳統親節點遍歷 | data-* 屬性方案 |
|---|---|
| 依賴具體 DOM 結構 (td 位置) | 數據與結構解耦 |
| 列表欄位增減需改 JS | 只需補 data-* 屬性 |
| 嵌套標籤易斷裂 | 直接讀取 dataset |
生產項目建議引入成熟彈層組件,代碼更簡潔:
// Layer 示例
$('.edit-btn').click(function() {
const data = $(this).data();
layer.open({
type: 1,
title: '編輯用戶',
content: $('#editFormTpl').html(), // 模板渲染
success: (layero) => {
layero.find('[name="id"]').val(data.id);
layero.find('[name="name"]').val(data.name);
// ...
},
yes: (index, layero) => {
$.post('update', layero.find('form').serialize(), (res) => {
if (res.success) { layer.close(index); location.reload(); }
});
}
});
});
**data-* 屬性 + 事件代理 + 原生 fetch** 實現零依賴、易維護的列表彈窗編輯。生產環境可替換為組件庫,核心數據傳遞邏輯不變。