Estoy tratando de ocultar el texto de un botón y mostrar una rueda giratoria en su lugar mientras proceso algo. Sin embargo, no se aplica ningún estilo a pesar de que las clases se alternan correctamente al valor correcto, como veo en las Herramientas para desarrolladores.
function toggleSpinner() { $('button span').toggleClass('display-none'); $('button .fa-spinner').toggleClass('display-none'); } function generateGrid(size) { let arr = getRandomNumbers(size); toggleSpinner(); // <--- HIDE TEXT SHOW SPINNER $('.grid').empty(); let html = ''; arr.forEach((element) => { html += '<div class="box">' + element.toLocaleString() + '</div>'; }); $('.grid').append(html); toggleSpinner(); // <--- HIDE SPINNER SHOW TEXT }Y este es mi HTML:
<button> <span>Generate Random Numbers</span> <i class="fas fa-spinner fa-spin display-none"></i> </button>Generar la cuadrícula a veces toma como 3 segundos dependiendo del tamaño de la cuadrícula y durante ese tiempo, me gustaría mostrar la flecha giratoria en el botón en lugar del texto.
La primera vez que se llama a toggleSpinner() , espero que el botón solo muestre un ícono giratorio sin el texto. La segunda vez que se llama a toggleSpinner() , espero que el icono giratorio desaparezca y que el texto del botón vuelva a aparecer.
Lo que sucede es que a pesar de que las clases se alternan, es como si el navegador no volviera a pintar la página hasta que todo esté hecho, es como si se llamara a toggleSpinner() dos veces, lo que da como resultado las mismas clases desde el principio, lo que no produce cambios. al botón.
Los cambios en el DOM no se procesan porque la generación de la cuadrícula bloquea el bucle de eventos. Por lo tanto, ejecutar generateGrid de forma asíncrona debería resolver el problema. Sin embargo, simplemente ejecutarlo como una función asíncrona y esperar a que termine (usando await ), o poner el código directamente en un controlador de promesa, corre el riesgo de poner el procesamiento en la cola de microtareas, lo que también bloquea el ciclo de eventos.
Este ejemplo simula "generar la cuadrícula" simplemente ejecutando un bucle durante 3 segundos. Se llama desde una llamada de temporizador con un argumento de devolución de llamada adicional para indicar cuándo ha terminado. La función de llamada, makeGrid devuelve una promesa que se cumple cuando se completa el procesamiento:
"use strict"; function toggleSpinner() { $('button span').toggleClass('display-none'); $('button .fa-spinner').toggleClass('display-none'); } function makeGrid(size) { toggleSpinner(); // show spinner return new Promise( resolve => { setTimeout( generateGrid, 0, size, function() { toggleSpinner(); // hide spinner resolve(); }); }); } function generateGrid( size, done) { // dummied code just to run synchonously for 3 seconds const end = Date.now() + 3000; while( Date.now() < end); done(); // callback when done } function test() { console.log( "calling makeGrid"); makeGrid(1).then( ()=>console.log("makeGrid finished")); } .display-none { display: none; } .fa-spinner:before { content: "⏳"; } <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js" crossorigin></script> <!-- body-html --> <button> <span>Generate Random Numbers</span> <i class="fas fa-spinner fa-spin display-none"></i> </button> <p> <button onclick="test()">test</button> <-- click here to testEs rudamentario y probablemente haya mejores formas, pero una opción es sacar el primer toggleSpinner del método y usar un tiempo de espera, que luego llama a su función generateGrid . Esto le da al CSS y a los conmutadores de clase tiempo para hacer lo suyo antes de que ocurra el bloqueo de procesamiento de generateGrid .
function toggleSpinner() { $('button span').toggleClass('display-none'); $('button .fa-spinner').toggleClass('display-none'); } function getRandomNumbers(size) { return new Array(size).fill('foo') } function generateGrid(size) { let arr = getRandomNumbers(size); $('.grid').empty(); let html = ''; arr.forEach((element) => { html += '<div class="box">' + element.toLocaleString() + '</div>'; }); $('.grid').append(html); toggleSpinner() } $('button').on('click', () => { toggleSpinner() setTimeout(() => { generateGrid(100000); }, 1000) }) .display-none { display: none; } <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <button> <span>Generate Random Numbers</span> <i class="fas fa-spinner fa-spin display-none">SPINNING</i> </button> <div class="grid"></div>