动态生成的表单提交按钮JavaScript失效问题求助
问题说明
页面初始存在的表单可正常提交,但通过JavaScript动态生成的表单点击提交按钮<button type="submit" class="generate-btn">Generate IA</button>时无任何响应。
动态生成表单的代码:
segundoDiv.innerHTML = ''; var nuevoElemento = document.createElement('section'); nuevoElemento.className = 'image-generator'; nuevoElemento.innerHTML = ` <div class="content"> <form action="#" class="generate-form"> <input class="prompt-input" type="text" placeholder="Describe what you want to see" required> <div class="controls"> <button type="submit" class="generate-btn">Generate IA</button> </div> </form> </div> <section class="image-gallery"> <div class="img-card hiddenia"><img src="images/img-1.jpg" alt="image"></div> </section>`; // Agregar el nuevo elemento dentro del segundo div segundoDiv.appendChild(nuevoElemento);
原脚本代码(未执行):
const generateForm = document.querySelector(".generate-form"); const generateBtn = generateForm.querySelector(".generate-btn"); const imageGallery = document.querySelector(".image-gallery"); const OPENAI_API_KEY = "12345678910"; // Your OpenAI API key here let isImageGenerating = false; const updateImageCard = (imgDataArray) => { imgDataArray.forEach((imgObject, index) => { const imgCard = imageGallery.querySelectorAll(".img-card")[index]; const imgElement = imgCard.querySelector("img"); const downloadBtn = imgCard.querySelector(".download-btn"); imgCard.classList.remove("hidden"); // Set the image source to the AI-generated image data const aiGeneratedImage = `data:image/jpeg;base64,${imgObject.b64_json}`; imgElement.src = aiGeneratedImage; // When the image is loaded, remove the loading class and set download attributes imgElement.onload = () => { imgCard.classList.remove("loading"); downloadBtn.setAttribute("href", aiGeneratedImage); downloadBtn.setAttribute("download", `${new Date().getTime()}.jpg`); } }); } const generateAiImages = async (userPrompt, userImgQuantity) => { try { // Send a request to the OpenAI API to generate images based on user inputs const response = await fetch("https://api.openai.com/v1/images/generations", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${OPENAI_API_KEY}`, }, body: JSON.stringify({ prompt: userPrompt, n: 1, size: "1024x1024", response_format: "b64_json" }), }); // Throw an error message if the API response is unsuccessful if(!response.ok) throw new Error("Failed to generate AI images. Make sure your API key is valid."); const { data } = await response.json(); // Get data from the response updateImageCard([...data]); } catch (error) { alert(error.message); } finally { generateBtn.removeAttribute("disabled"); generateBtn.innerText = "Generate"; isImageGenerating = false; } } const handleImageGeneration = (e) => { e.preventDefault(); if(isImageGenerating) return; // Get user input and image quantity values const userPrompt = e.srcElement[0].value; const userImgQuantity = 1; // Disable the generate button, update its text, and set the flag generateBtn.setAttribute("disabled", true); generateBtn.innerText = "Generating"; isImageGenerating = true; // Creating HTML markup for image cards with loading state const imgCardMarkup = Array.from({ length: userImgQuantity }, () => `<div class="img-card loading"> <img src="images/loader.svg" alt="AI generated image"> <a class="download-btn" href="#"> <img src="images/download.svg" alt="download icon"> </a> </div>` ).join(""); imageGallery.innerHTML = imgCardMarkup; generateAiImages(userPrompt, userImgQuantity); } generateForm.addEventListener("submit", handleImageGeneration);
问题原因
原脚本在页面加载时就执行document.querySelector(".generate-form")获取表单并绑定submit事件,但动态生成的表单此时还未被创建,导致事件只绑定到了页面初始的表单上,新生成的表单没有触发事件的绑定。
同时,原脚本中generateBtn、imageGallery等变量都是页面加载时获取的初始元素,动态生成新表单后,这些变量指向的还是旧元素,无法操作新生成的按钮和图片容器。
解决方案
方法1:使用事件委托(推荐)
将事件绑定到已存在的父元素(比如document或segundoDiv),通过事件冒泡机制监听submit事件,判断触发事件的表单是否是目标动态表单。这种方法无需关心表单何时生成,都能触发事件。
修改原脚本的事件绑定部分,并调整相关函数以适配动态元素:
- 替换原有的事件绑定代码:
// 移除原来的 generateForm.addEventListener("submit", handleImageGeneration); // 改为事件委托 document.addEventListener('submit', function(e) { if (e.target.classList.contains('generate-form')) { handleImageGeneration(e); } });
- 修改
handleImageGeneration函数,从事件目标获取当前表单及相关元素:
const handleImageGeneration = (e) => { e.preventDefault(); // 获取当前提交的表单 const currentForm = e.target; const generateBtn = currentForm.querySelector(".generate-btn"); // 获取当前表单对应的图片容器 const imageGallery = currentForm.closest('.image-generator').querySelector(".image-gallery"); if(isImageGenerating) return; const userPrompt = currentForm.querySelector(".prompt-input").value; const userImgQuantity = 1; generateBtn.setAttribute("disabled", true); generateBtn.innerText = "Generating"; isImageGenerating = true; const imgCardMarkup = Array.from({ length: userImgQuantity }, () => `<div class="img-card loading"> <img src="images/loader.svg" alt="AI generated image"> <a class="download-btn" href="#"> <img src="images/download.svg" alt="download icon"> </a> </div>` ).join(""); imageGallery.innerHTML = imgCardMarkup; // 将当前按钮和容器作为参数传入 generateAiImages(userPrompt, userImgQuantity, generateBtn, imageGallery); }
- 修改
generateAiImages函数,接收动态元素参数:
const generateAiImages = async (userPrompt, userImgQuantity, generateBtn, imageGallery) => { try { const response = await fetch("https://api.openai.com/v1/images/generations", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${OPENAI_API_KEY}`, }, body: JSON.stringify({ prompt: userPrompt, n: 1, size: "1024x1024", response_format: "b64_json" }), }); if(!response.ok) throw new Error("Failed to generate AI images. Make sure your API key is valid."); const { data } = await response.json(); // 传入当前图片容器 updateImageCard([...data], imageGallery); } catch (error) { alert(error.message); } finally { generateBtn.removeAttribute("disabled"); generateBtn.innerText = "Generate"; isImageGenerating = false; } }
- 修改
updateImageCard函数,接收图片容器参数:
const updateImageCard = (imgDataArray, imageGallery) => { imgDataArray.forEach((imgObject, index) => { const imgCard = imageGallery.querySelectorAll(".img-card")[index]; const imgElement = imgCard.querySelector("img"); const downloadBtn = imgCard.querySelector(".download-btn"); imgCard.classList.remove("hidden"); const aiGeneratedImage = `data:image/jpeg;base64,${imgObject.b64_json}`; imgElement.src = aiGeneratedImage; imgElement.onload = () => { imgCard.classList.remove("loading"); downloadBtn.setAttribute("href", aiGeneratedImage); downloadBtn.setAttribute("download", `${new Date().getTime()}.jpg`); } }); }
方法2:动态生成后绑定事件
在动态生成表单并添加到DOM后,立即获取新表单并绑定submit事件。适合仅单次动态生成表单的场景:
在segundoDiv.appendChild(nuevoElemento);之后添加以下代码:
// 获取新生成的表单 const newGenerateForm = nuevoElemento.querySelector(".generate-form"); // 绑定提交事件 newGenerateForm.addEventListener("submit", handleImageGeneration);
同时,需要修改handleImageGeneration、generateAiImages、updateImageCard函数,确保操作的是当前表单对应的元素(参考方法1中的函数修改逻辑),避免使用页面加载时获取的旧元素变量。
内容的提问来源于stack exchange,提问作者luis manga

