<input type="file" name="uploadFile" onchange="javascript:my_check(this.value);"/>
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>檔案上傳類型檢查</title>
<style>
.hidden { display: none; }
</style>
</head>
<body>
<form id="uploadForm" method="post" enctype="multipart/form-data">
<input type="file" id="fileInput" name="uploadFile" accept="image/jpeg,image/png,image/gif" />
<div id="submitBtnContainer" class="hidden">
<input type="submit" value="上傳" />
</div>
<div id="errorMsg" class="hidden" style="color:red;"></div>
</form>
<script>
// DOMContentLoaded 確保元素存在再綁定事件 (最佳實務)
document.addEventListener('DOMContentLoaded', function() {
const fileInput = document.getElementById('fileInput');
const submitBtnContainer = document.getElementById('submitBtnContainer');
const errorMsg = document.getElementById('errorMsg');
fileInput.addEventListener('change', function() {
const file = this.files[0];
if (!file) return; // 使用者取消選擇
// 1. 檢查副檔名 (雙重保險:accept 屬性 + JS 檢查)
const allowedExt = ['.jpg', '.jpeg', '.png', '.gif'];
const fileName = file.name.toLowerCase();
const ext = fileName.substring(fileName.lastIndexOf('.'));
if (allowedExt.includes(ext)) {
// 合法:顯示上傳按鈕、隱藏錯誤
submitBtnContainer.classList.remove('hidden');
errorMsg.classList.add('hidden');
errorMsg.textContent = '';
} else {
// 非法:隱藏按鈕、顯示錯誤、清空檔案選擇
submitBtnContainer.classList.add('hidden');
errorMsg.classList.remove('hidden');
errorMsg.textContent = '非法圖片類型!僅允許 JPG, PNG, GIF';
this.value = ''; // 清空 input
}
});
});
</script>
</body>
</html>
| 原始錯誤 | 修正做法 | 說明 |
|---|---|---|
| `onchange="javascript:my_check(...)"` | `addEventListener('change', ...)` | 分離結構與行為、避免全域汙染、支援多監聽器 |
| `obj.innerHTML("...")` | `element.classList.toggle('hidden')` / `textContent` | `innerHTML` 是屬性;操作 CSS 類別比拼接 HTML 字串更安全、可維護 |
| `document.write("錯誤")` | 顯示預留錯誤區塊 `errorMsg` | `document.write` 頁面載入後呼叫會清空整個文檔 |
| 無 `accept` 屬性 | `accept="image/jpeg,image/png,image/gif"` | 瀏覽器原生過濾檔案對話框,減少使用者錯誤選擇 |
| 依賴 `this.value` 取副檔名 | `file.name` (File API) | `this.value` 有偽路徑 `C:\fakepath\`,`file.name` 直接得純檔名 |
// 檢查檔案實際 MIME 類型 (防止偽裝副檔名)
const allowedMime = ['image/jpeg', 'image/png', 'image/gif'];
if (!allowedMime.includes(file.type)) {
// 類型不符
}
⚠ `file.type` 由瀏覽器根據副檔名推斷,**非內容檢驗**。伺服器端**必須**再次驗證 (魔數/Magic Bytes、圖片解碼庫)。
`onchange` 無法呼叫函數主因:**語法錯誤 (`javascript:`、`innerHTML()`)、API 使用錯誤 (`document.write`)、邏輯缺陷**
修正核心:**事件監聽器綁定 + File API (`files[0].name`) + CSS 類別切換 UI 狀態 + `accept` 屬性輔助**。