I have a kind of form where questions and answers are added for a test, all my functions perform their task according to my expectations except one of them which is called several times each time a button is pressed (it was the solution I found so that when new elements are created in the DOM loop back through the new elements)
The function adds new possible answers to the DOM but the problem I have is that as new question fields are created, the elements that are left behind repeat the function more than necessary. For example, I have 3 question fields, if I add 1 new answer to the last field an answer is inserted (as it should be) but in the second the answer is inserted 2 times and in the first 3 times, I suppose this has to do with the fact that I call the function that checks the click on the different elements every time a new question field is inserted.
Curiously, other functions that I call every time a new question field is created do not execute their functionality more times than expected, it only happens to me with the function to add answers. If you have not understood me, ask me.
I share code below:
HTML and PHP
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="/css/admin/tests/crearTest.css">
</head>
<body>
<main class="test">
<div class="contenedor">
<div class="contenedor__nombre">
<p>Nombre del Test</p>
<input type="text" name="nombreTest" id="nombreTest" class="inputTexto">
</div>
<div class="contenedor__temarioPerteneciente">
<p>Seleccione Temario al que Pertenece</p>
<select name="temarioPerteneciente" id="temarioPerteneciente">
<option value="">Ninguna Opción Seleccionada</option>
<?php
while ($row = mysqli_fetch_array($consultarTemarios))
{
$id = $row['id_temario'];
$nombre = $row['nombre_temario'];
echo '<option value="' . $id . '">' . $nombre . '</option>';
}
?>
</select>
</div>
</div>
</main>
<div id="contenedorVistasPrevias">
<div class="vistaPrevia" id="vistaPrevia-1" numvistaprevia="1">
<div class="vistaPrevia__pregunta">
<p>Nombre de la Pregunta</p>
<input type="text" name="" id="nombrePregunta-1" class="inputTexto textoPregunta">
<p>Añadir Respuesta</p>
<input type="text" name="" id="añadirRespuesta-1" class="inputTexto textoRespuesta">
<button class="vistaPrevia__pregunta__boton botonDesactivado btnAñadir" id="btnAñadirRespuesta-1">Añadir</button>
<p>Escoja la respuesta correcta</p>
</div>
<div class="vistaPrevia__respuestas">
<div class="vistaPrevia__respuestas__radio radioRespuestas" id="vistaPreviaRespuestasRadio-1">
</div>
</div>
</div>
</div>
<div class="contenedorEnviarTest">
<button class="agregarPregunta botonDesactivado" id="btnAgregarPregunta">Agregar Pregunta</button>
</div>
<div class="contenedorEnviarTest">
<button class="enviarTest botonDesactivado" id="btnEnviarTest">Enviar Test</button>
</div>
<script src="/js/admin/test/peticionTest.js"></script>
</body>
</html>
javascript
/*=============================================
= Validar Agregar Pregunta =
=============================================*/
let nombreTest = document.getElementById("nombreTest");
let select = document.getElementById("temarioPerteneciente");
const btnAgregarPregunta = document.getElementById("btnAgregarPregunta");
let valueSelect = "";
if (nombreTest.textContent === "") {
btnAgregarPregunta.disabled = true;
}
select.addEventListener("change", () => {
let opcionSeleccionada = select.options[select.selectedIndex];
let idTemarioSeleccionado = opcionSeleccionada.value;
valueSelect = idTemarioSeleccionado;
// idTemarioCorrespondiente.value = idTemarioSeleccionado;
console.log(idTemarioSeleccionado);
if (nombreTest.value !== "" && valueSelect !== "") {
btnAgregarPregunta.disabled = false;
btnAgregarPregunta.classList.remove("botonDesactivado");
} else {
btnAgregarPregunta.disabled = true;
btnAgregarPregunta.classList.add("botonDesactivado");
}
validarTestCompleto();
});
nombreTest.onkeyup = () => {
if (nombreTest.value !== "" && valueSelect !== "") {
btnAgregarPregunta.disabled = false;
btnAgregarPregunta.classList.remove("botonDesactivado");
} else {
btnAgregarPregunta.disabled = true;
btnAgregarPregunta.classList.add("botonDesactivado");
}
validarTestCompleto();
};
/*============ End of Validar Agregar Pregunta =============*/
/*=============================================
= Validar Añadir Respuesta =
=============================================*/
validarAñadirRespuesta = () => {
let añadirRespuesta = document.querySelectorAll(".textoRespuesta");
let nombrePregunta = document.querySelectorAll(".textoPregunta");
let btnAñadir = document.querySelectorAll(".btnAñadir");
añadirRespuesta.forEach((elementoActual, indice) => {
elementoActual.onkeyup = function () {
if (
añadirRespuesta[indice].value !== "" &&
nombrePregunta[indice].value !== ""
) {
btnAñadir[indice].disabled = false;
btnAñadir[indice].classList.remove("botonDesactivado");
} else {
btnAñadir[indice].disabled = true;
btnAñadir[indice].classList.add("botonDesactivado");
}
validarTestCompleto();
};
});
nombrePregunta.forEach((elementoActual, indice) => {
elementoActual.onkeyup = function () {
if (
añadirRespuesta[indice].value !== "" &&
nombrePregunta[indice].value !== ""
) {
btnAñadir[indice].disabled = false;
btnAñadir[indice].classList.remove("botonDesactivado");
} else {
btnAñadir[indice].disabled = true;
btnAñadir[indice].classList.add("botonDesactivado");
}
validarTestCompleto();
};
});
};
validarAñadirRespuesta();
/*============ End of Validar Añadir Respuesta =============*/
/*=============================================
= Añadir Respuesta =
=============================================*/
let numRadioLabel = 1;
añadiendoRespuesta = (indice) => {
let añadirRespuesta = document.querySelectorAll(".textoRespuesta");
let radioRespuestas = document.querySelectorAll(".radioRespuestas");
let radioRespuesta = `
<div class="vistaPrevia__respuestas__radio__elemento" numrespuesta="${numRadioLabel}">
<div class="vistaPrevia__respuestas__radio__elemento__contenedor">
<div class="radio-block">
<div class="radio-content">
<input id="radio${numRadioLabel}" type="radio" name="radio-${
indice + 1
}" class="radio"/>
<label for="radio${numRadioLabel}" class="respuesta"><span></span></label>
<p class="respuesta">${
añadirRespuesta[indice].value
}</p>
</div>
</div>
</div>
<div class="eliminarRespuesta">
<button class="eliminarRespuesta__botón" numrespuesta="${numRadioLabel}" >Eliminar</button>
</div>
</div>
`;
radioRespuestas[indice].insertAdjacentHTML("beforeend", radioRespuesta);
numRadioLabel++;
botonesRadio();
botonesEliminarRespuesta();
console.log("añadiendi");
};
añadirRespuesta = () => {
btnAñadirRespuesta = document.querySelectorAll(".btnAñadir");
btnAñadirRespuesta.forEach((botonActual, indice) => {
botonActual.addEventListener("click", () => {
añadiendoRespuesta(indice);
});
});
};
añadirRespuesta();
/*============ End of Añadir Respuesta =============*/
/*=============================================
= Agregar Pregunta =
=============================================*/
var numID = 2;
btnAgregarPregunta.addEventListener("click", () => {
let contenedorVistasPrevias = document.getElementById(
"contenedorVistasPrevias"
);
let nuevaPregunta = `
<div class="vistaPrevia" id="vistaPrevia-${numID}" numvistaprevia="${numID}">
<div class="vistaPrevia__pregunta">
<p>Nombre de la Pregunta</p>
<input type="text" name="" id="nombrePregunta-${numID}" class="inputTexto textoPregunta">
<p>Añadir Respuesta</p>
<input type="text" name="" id="añadirRespuesta-${numID}" class="inputTexto textoRespuesta">
<button class="vistaPrevia__pregunta__boton botonDesactivado btnAñadir" id="btnAñadirRespuesta-1">Añadir</button>
<p>Escoja la respuesta correcta</p>
</div>
<div class="vistaPrevia__respuestas">
<div class="vistaPrevia__respuestas__radio radioRespuestas" id="vistaPreviaRespuestasRadio-${numID}">
</div>
</div>
<div class="eliminarPregunta">
<button class="eliminarPregunta__botón" numvistaprevia="${numID}">Eliminar Pregunta</button>
</div>
</div>
`;
contenedorVistasPrevias.insertAdjacentHTML("beforeend", nuevaPregunta);
numID++;
validarAñadirRespuesta();
añadirRespuesta();
validarTestCompleto();
botonesEliminarPregunta();
});
/*============ End of Agregar Pregunta =============*/
/*=============================================
= Validar Test =
=============================================*/
let botonAgregarPregunta = document.getElementById("btnAgregarPregunta");
const btnEnviarTest = document.getElementById("btnEnviarTest");
validarAgregarPregunta = () => {
if (botonAgregarPregunta.classList.contains("botonDesactivado")) {
botonAgregarPreguntaValido--;
} else {
botonAgregarPreguntaValido++;
}
};
validarTestCompleto = () => {
let botonesRadio = document.querySelectorAll(".radio");
let botonesAñadir = document.querySelectorAll(".btnAñadir");
botonesRadioActivos = 0;
botonesAñadirValidos = 0;
botonAgregarPreguntaValido = 0;
for (let i = 0; i < botonesRadio.length; i++) {
if (botonesRadio[i].checked === true) {
botonesRadioActivos++;
} else {
}
}
for (let i = 0; i < botonesAñadir.length; i++) {
if (botonesAñadir[i].classList.contains("botonDesactivado")) {
botonesAñadirValidos--;
} else {
botonesAñadirValidos++;
}
}
validarAgregarPregunta();
if (
botonesRadioActivos === numID - 1 &&
botonesAñadirValidos === numID - 1 &&
botonAgregarPreguntaValido > 0
) {
btnEnviarTest.classList.remove("botonDesactivado");
btnEnviarTest.disabled = false;
} else {
btnEnviarTest.classList.add("botonDesactivado");
btnEnviarTest.disabled = true;
}
};
botonesRadio = () => {
let botonesRadio = document.querySelectorAll(".radio");
botonesRadio.forEach((botonRadio) => {
botonRadio.addEventListener("click", () => {
validarTestCompleto();
});
});
};
btnEnviarTest.disabled = true;
/*============ End of Validar Test =============*/
/*=============================================
= Enviar Datos =
=============================================*/
respuestasCorrectas = [];
obtenerRespuestasCorrectas = () => {
let botonesRadio = document.querySelectorAll(".radio");
for (let i = 0; i < botonesRadio.length; i++) {
if (botonesRadio[i].checked === true) {
respuestasCorrectas.push(
botonesRadio[i].nextElementSibling.nextElementSibling.innerHTML
);
console.log(respuestasCorrectas);
}
}
};
/* End of Subsection
-------------------------------------------------- */
valorNombrePreguntas = [];
obtenerNombrePreguntas = () => {
let nombrePregunta = document.querySelectorAll(".textoPregunta");
for (let i = 0; i < nombrePregunta.length; i++) {
valorNombrePreguntas.push(nombrePregunta[i].value);
console.log(valorNombrePreguntas);
}
};
/* End of Subsection
-------------------------------------------------- */
btnEnviarTest.addEventListener("click", () => {
let nombreDelTest = nombreTest.value;
let temarioAlQuePertenece = valueSelect;
obtenerRespuestasCorrectas();
obtenerNombrePreguntas();
let enviarTest = new FormData();
enviarTest.append("nombreDelTest", nombreDelTest);
enviarTest.append("temarioAlQuePertenece", temarioAlQuePertenece);
enviarTest.append(
"valorNombrePreguntas",
JSON.stringify(valorNombrePreguntas)
);
enviarTest.append(
"respuestasCorrectas",
JSON.stringify(respuestasCorrectas)
);
console.log(`respuestas correctas ${respuestasCorrectas}`);
xhr = new XMLHttpRequest();
xhr.open("POST", "/src/admin/test/subirTest.php", true);
xhr.onload = function () {
if (this.status === 200) {
console.log("exito");
}
};
xhr.send(enviarTest);
});
/*============ End of Enviar Datos =============*/
/*=============================================
= Eliminar Pregunta =
=============================================*/
eliminarPregunta = (numVistaPrevia) => {
console.log("click");
let vistaPrevias = document.querySelectorAll(".vistaPrevia");
numVistaPrevia = numVistaPrevia;
for (let i = 0; i < vistaPrevias.length; i++) {
if (vistaPrevias[i].getAttribute("numVistaPrevia") === numVistaPrevia) {
vistaPrevias[i].remove();
numID--;
validarTestCompleto();
}
}
};
botonesEliminarPregunta = () => {
let eliminarPreguntaBtn = document.querySelectorAll(
".eliminarPregunta__botón"
);
eliminarPreguntaBtn.forEach((elemento) => {
elemento.addEventListener("click", () => {
numVistaPrevia = elemento.getAttribute("numVistaPrevia");
eliminarPregunta(numVistaPrevia);
console.log(numVistaPrevia);
});
});
};
botonesEliminarPregunta();
/*============ End of Eliminar Pregunta =============*/
/*=============================================
= Eliminar Respuesta =
=============================================*/
eliminarRespuesta = (numRespuesta) => {
console.log("click");
let elementosRadio = document.querySelectorAll(
".vistaPrevia__respuestas__radio__elemento"
);
numRespuesta = numRespuesta;
for (let i = 0; i < elementosRadio.length; i++) {
if (elementosRadio[i].getAttribute("numrespuesta") === numRespuesta) {
elementosRadio[i].remove();
validarTestCompleto();
}
}
};
botonesEliminarRespuesta = () => {
let eliminarRespuestaBtn = document.querySelectorAll(
".eliminarRespuesta__botón"
);
eliminarRespuestaBtn.forEach((elemento) => {
elemento.addEventListener("click", () => {
numRespuesta = elemento.getAttribute("numrespuesta");
eliminarRespuesta(numRespuesta);
console.log(numRespuesta);
});
});
};
/*============ End of Eliminar Respuesta =============*/
The section of the function to add questions is where I have the problem, the rest of the functions are executed as expected. I appreciate any help or suggestion.
If anyone knows how to execute a function on existing and dynamically created elements without giving me that error mentioned above I would appreciate it.
Thanks in advance.
I have the answer. I found a method in this article where it contains a link to this page . In short, the answer is in these lines of code, apart from some changes I made to the code:
Now, if someone can clarify for me why it doesn't work when using the EcmaScript function style and how it works, I'd really appreciate it.