Me gustaría hacer botones en mi HTML mientras una de mis clases JS se está ejecutando y recibe una respuesta del backend. Estoy usando jQuery y vanilla JS y creé un ejemplo a continuación. En este ejemplo, me gustaría deshabilitar dinámicamente el botón en el que no se hizo clic para mostrarle al usuario que algo está sucediendo y que no debe hacer clic en ningún otro lugar hasta que se devuelva una respuesta.
Array.from(document.getElementsByClassName("upload-button")).forEach(button => { button.addEventListener("click", function(){ let buttonSpinner = this.getElementsByTagName("span")[0]; buttonSpinner.classList.remove("d-none"); // here I would like to disable the button that wasn't selected let destination = this.value; uploadData(destination, buttonSpinner); } ) }) function uploadData(destination, buttonSpinner){ let link = "https://www.google.com/"; let description = "Stack Overflow"; new Upload(link, description, destination, buttonSpinner) } class Upload { constructor(link=null, description=null, destination="submit", buttonSpinner=null){ this.link = link; this.description = description; this.destination = destination; this.buttonSpinner = buttonSpinner; this.log(); this.disableButtonSpinner(); } log(){ console.log(this.link) } wait(){ return new Promise(resolve => setTimeout(resolve, 3000)); // wait for data to return from backend } async disableButtonSpinner(){ await this.wait() // simulate backend returning data this.buttonSpinner.classList.add("d-none"); } } <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <div> <button class="btn btn-primary upload-button" id="upload-user-data-button" value="data"> Upload & View My Data <span class="ms-2 spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span> <span class="visually-hidden">Loading...</span> </button> <button class="btn btn-primary upload-button" id="upload-submit-button" value="submit"> Upload & Submit <span class="ms-2 spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span> <span class="visually-hidden">Loading...</span> </button> </div>Podría hacer algo así, recorrer todos los botones para deshabilitarlos/rehabilitarlos al hacer clic.
Array.from(document.getElementsByClassName("upload-button")).forEach((button, index) => { button.addEventListener("click", function(){ let buttonSpinner = this.getElementsByTagName("span")[0]; buttonSpinner.classList.remove("d-none"); // here I would like to disable the button that wasn't selected let buttons = Array.from(document.getElementsByClassName("upload-button")); buttons.forEach((button, i)=>{ i!==index?button.disabled=true:null; }) let destination = this.value; uploadData(destination, buttonSpinner); } ) }) function uploadData(destination, buttonSpinner){ let link = "https://www.google.com/"; let description = "Stack Overflow"; new Upload(link, description, destination, buttonSpinner) } class Upload { constructor(link=null, description=null, destination="submit", buttonSpinner=null){ this.link = link; this.description = description; this.destination = destination; this.buttonSpinner = buttonSpinner; this.log(); this.disableButtonSpinner(); } log(){ console.log(this.link) } wait(){ return new Promise(resolve => setTimeout(resolve, 3000)); // wait for data to return from backend } async disableButtonSpinner(){ await this.wait() // simulate backend returning data this.buttonSpinner.classList.add("d-none"); let buttons = Array.from(document.getElementsByClassName("upload-button")) buttons.forEach(button=>button.disabled=false) } } <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <div> <button class="btn btn-primary upload-button" id="upload-user-data-button" value="data"> Upload & View My Data <span class="ms-2 spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span> <span class="visually-hidden">Loading...</span> </button> <button class="btn btn-primary upload-button" id="upload-submit-button" value="submit"> Upload & Submit <span class="ms-2 spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span> <span class="visually-hidden">Loading...</span> </button> </div>Puede agregar un parámetro en la función para acceder al evento de clic que usé e. Luego puede recorrer sus botones y deshabilitar otros que no sean el objetivo de eventos. Esto se logra mediante Array.from(document.getElementsByClassName("upload-button")).forEach(insideButton => { if(e.target !== insideButton) insideButton.disabled = true});
Array.from(document.getElementsByClassName("upload-button")).forEach(button => { button.addEventListener("click", function(e){ let buttonSpinner = this.getElementsByTagName("span")[0]; buttonSpinner.classList.remove("d-none"); // here I would like to disable the button that wasn't selected Array.from(document.getElementsByClassName("upload-button")).forEach(insideButton => { if(e.target !== insideButton) insideButton.disabled = true}); let destination = this.value; uploadData(destination, buttonSpinner); } ) }) function uploadData(destination, buttonSpinner){ let link = "https://www.google.com/"; let description = "Stack Overflow"; new Upload(link, description, destination, buttonSpinner) } class Upload { constructor(link=null, description=null, destination="submit", buttonSpinner=null){ this.link = link; this.description = description; this.destination = destination; this.buttonSpinner = buttonSpinner; this.log(); this.disableButtonSpinner(); } log(){ console.log(this.link) } wait(){ return new Promise(resolve => setTimeout(resolve, 3000)); // wait for data to return from backend } async disableButtonSpinner(){ await this.wait() // simulate backend returning data this.buttonSpinner.classList.add("d-none"); } } <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <div> <button class="btn btn-primary upload-button" id="upload-user-data-button" value="data"> Upload & View My Data <span class="ms-2 spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span> <span class="visually-hidden">Loading...</span> </button> <button class="btn btn-primary upload-button" id="upload-submit-button" value="submit"> Upload & Submit <span class="ms-2 spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span> <span class="visually-hidden">Loading...</span> </button> </div>agregando esta solución para solucionar el problema con la solución @Raven donde los botones permanecen deshabilitados incluso después de que se realiza la operación.
Array.from(document.getElementsByClassName("upload-button")).forEach(button => { button.addEventListener("click", async function(e){ let buttonSpinner = this.getElementsByTagName("span")[0]; buttonSpinner.classList.remove("d-none"); // here I would like to disable the button that wasn't selected Array.from(document.getElementsByClassName("upload-button")).forEach(insideButton => { if(e.target !== insideButton) insideButton.disabled = true} ); let destination = this.value; await uploadData(destination, buttonSpinner); } ) }) async function uploadData(destination, buttonSpinner){ let link = "https://www.google.com/"; let description = "Stack Overflow"; await new Upload(link, description, destination, buttonSpinner) } class Upload { constructor(link=null, description=null, destination="submit", buttonSpinner=null){ this.link = link; this.description = description; this.destination = destination; this.buttonSpinner = buttonSpinner; this.log(); this.disableButtonSpinner(); } log(){ console.log(this.link) } wait(){ return new Promise(resolve => setTimeout(resolve, 3000)); // wait for data to return from backend } async disableButtonSpinner(){ await this.wait().then(()=>{ Array.from(document.getElementsByClassName("upload-button")).forEach(insideButton => { insideButton.disabled = false; } ); }) // simulate backend returning data this.buttonSpinner.classList.add("d-none"); } } <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <div> <button class="btn btn-primary upload-button" id="upload-user-data-button" value="data"> Upload & View My Data <span class="ms-2 spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span> <span class="visually-hidden">Loading...</span> </button> <button class="btn btn-primary upload-button" id="upload-submit-button" value="submit"> Upload & Submit <span class="ms-2 spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span> <span class="visually-hidden">Loading...</span> </button> </div>