Fetch重复执行致项目多次创建的问题排查与解决求助
问题根源
你遇到的重复创建问题,核心原因是每次调用verifierboutonValider时,尝试移除的是匿名函数,但匿名函数无法被精准匹配移除,导致之前添加的submit监听一直残留。同时每次符合validPhoto && validTitre && validCategorie条件时,又会新增一个submit监听,最终监听数量随verifierboutonValider调用次数递增,提交表单时就会触发多次enregistrerProjet。
解决方案
方案一:提取命名回调函数(精准管理监听)
把submit监听的回调改成命名函数,让removeEventListener可以精准定位并移除旧监听,避免叠加:
// 提取命名回调函数,统一处理表单提交逻辑 async function handleFormSubmit(e) { e.preventDefault(); // 二次校验防止状态变化导致误提交 if (validPhoto && validTitre && validCategorie) { await enregistrerProjet(imageInput, titleInput, categoryInput); } } // ******************************* FONCTION ENREGISTREMENT DU NOUVEAU PROJET ************************************* let errorMessage = document.getElementById("errorMessage"); async function enregistrerProjet(imageInput, titleInput, categoryInput) { const formData = new FormData(); formData.append("image", imageInput); formData.append("title", titleInput); formData.append("category", categoryInput); try { const response = await fetch("http://localhost:5678/api/works", { method: "POST", headers: { "accept": "application/json", "Authorization": "Bearer " + tokenLogin, }, body: formData, }) const responseProjet = await response.json(); if (!response.ok) { throw new Error(`Une erreur s'est produite lors de l'ajout d'un élément (${response.status}). Veuillez réessayer plus tard.`) } projets.push(responseProjet); // 优化DOM操作:直接重新生成画廊,无需先添加再清空 document.querySelector(".gallery").innerHTML = ""; genererProjets(projets); document.getElementById("modifyProject").className = "modal-masque"; document.getElementById("galeriePhoto").className = "modal-wrapper"; masquerModaleAjoutphoto(); } catch (error) { errorMessage.textContent = "Erreur d'envoi du projet."; // 优化错误监听:用命名函数+once选项,避免监听叠加 const clearError = (event) => { event.preventDefault(); errorMessage.textContent = ""; // 触发后自动移除监听 document.removeEventListener("keydown", clearError); document.removeEventListener("click", clearError); }; document.addEventListener("keydown", clearError, { once: true }); document.addEventListener("click", clearError, { once: true }); alert(error); }; }; // ********************** FONCTION ACTIVATION/DESACTIVATION DU BOUTON DE VALIDATION **************************** function verifierboutonValider() { const boutonValidationprojet = document.getElementById("boutonValidprojet"); const formulaire = document.getElementById("formulaire"); // 移除旧的命名回调监听 formulaire.removeEventListener("submit", handleFormSubmit); if (validPhoto && validTitre && validCategorie) { boutonValidationprojet.className = "btn-on_2"; // 添加命名回调监听 formulaire.addEventListener("submit", handleFormSubmit); } else { boutonValidationprojet.className = "btn-off_2"; }; };
方案二:初始化单次监听(更简洁推荐)
直接在页面初始化时给表单添加一次submit监听,verifierboutonValider只负责切换按钮样式,完全不用处理监听的添加/移除:
// 页面初始化时添加一次表单提交监听 document.getElementById("formulaire").addEventListener("submit", async function(e) { e.preventDefault(); // 仅当校验通过时执行提交逻辑 if (validPhoto && validTitre && validCategorie) { await enregistrerProjet(imageInput, titleInput, categoryInput); } }); // ******************************* FONCTION ENREGISTREMENT DU NOUVEAU PROJET ************************************* let errorMessage = document.getElementById("errorMessage"); async function enregistrerProjet(imageInput, titleInput, categoryInput) { // 原函数内容保持不变,仅优化错误监听和DOM操作(同方案一) const formData = new FormData(); formData.append("image", imageInput); formData.append("title", titleInput); formData.append("category", categoryInput); try { const response = await fetch("http://localhost:5678/api/works", { method: "POST", headers: { "accept": "application/json", "Authorization": "Bearer " + tokenLogin, }, body: formData, }) const responseProjet = await response.json(); if (!response.ok) { throw new Error(`Une erreur s'est produite lors de l'ajout d'un élément (${response.status}). Veuillez réessayer plus tard.`) } projets.push(responseProjet); document.querySelector(".gallery").innerHTML = ""; genererProjets(projets); document.getElementById("modifyProject").className = "modal-masque"; document.getElementById("galeriePhoto").className = "modal-wrapper"; masquerModaleAjoutphoto(); } catch (error) { errorMessage.textContent = "Erreur d'envoi du projet."; const clearError = (event) => { event.preventDefault(); errorMessage.textContent = ""; document.removeEventListener("keydown", clearError); document.removeEventListener("click", clearError); }; document.addEventListener("keydown", clearError, { once: true }); document.addEventListener("click", clearError, { once: true }); alert(error); }; }; // ********************** FONCTION ACTIVATION/DESACTIVATION DU BOUTON DE VALIDATION **************************** function verifierboutonValider() { const boutonValidationprojet = document.getElementById("boutonValidprojet"); // 只负责按钮样式切换,不再处理监听逻辑 if (validPhoto && validTitre && validCategorie) { boutonValidationprojet.className = "btn-on_2"; } else { boutonValidationprojet.className = "btn-off_2"; }; };
额外优化说明
- 错误处理中的监听:原代码每次报错都会新增keydown/click监听,改成命名函数+
{ once: true }后,监听触发一次会自动移除,避免残留。 - DOM操作冗余:原函数中先添加新元素再清空画廊,直接调用
genererProjets(projets)即可完成刷新,无需重复操作。
内容的提问来源于stack exchange,提问作者Fred2460
相关产品推荐
相关产品推荐

