web-dev-qa-db-fra.com

jQuery/Javascript - Comment attendre que le DOM manipulé soit mis à jour avant de continuer avec function

Ce que j'essaie de faire est de mettre à jour un simple div en disant "Traitement en cours ..." avant d'exécuter un script gourmand en ressources processeur (cela prend 3-12 secondes, sans AJAX), puis de mettre à jour le div en disant "Terminé! " lorsque vous avez terminé. 

Ce que je vois, c'est que la div ne se met jamais à jour avec "Processing ...". Si je définis un point d'arrêt immédiatement après cette commande, le texte div est mis à jour, je sais donc que la syntaxe est correcte. Même comportement dans IE9, FF6, Chrome13.

Même en contournant jQuery et en utilisant du Javascript brut de base, je vois le même problème.

Vous penseriez que cela aurait une réponse facile. Cependant, étant donné que jQuery .html () et .text () n'ont pas de hook de rappel, ce n'est pas une option. De plus, il n'est pas animé, il n'y a donc pas de file d'attente à manipuler.

Vous pouvez le tester vous-même en utilisant l'exemple de code que j'ai préparé ci-dessous, qui montre à la fois les implémentations jQuery et Javascript avec une fonction de processeur élevé de 5 secondes. Le code est facile à suivre. Lorsque vous cliquez sur le bouton ou sur le lien, vous ne voyez jamais "Traitement en cours ..."

<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" ></script>
<script type="text/javascript">
function addSecs(d, s) {return new Date(d.valueOf()+s*1000);}
function doRun() {
    document.getElementById('msg').innerHTML = 'Processing JS...';
    start = new Date();
    end = addSecs(start,5);
    do {start = new Date();} while (end-start > 0);
    document.getElementById('msg').innerHTML = 'Finished JS';   
}
$(function() {
    $('button').click(function(){
        $('div').text('Processing JQ...');  
        start = new Date();
        end = addSecs(start,5);
        do {start = new Date();} while (end-start > 0);
        $('div').text('Finished JQ');   
    });
});
</script>
</head>
<body>
    <div id="msg">Not Started</div>
    <button>jQuery</button>
    <a href="#" onclick="doRun()">javascript</a>
</body>
</html>
23
pcormier

définissez-le sur processing, puis effectuez setTimeout pour empêcher la tâche intensive du processeur de s'exécuter avant la mise à jour de la div.

<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" ></script>
<script>
function addSecs(d, s) {return new Date(d.valueOf()+s*1000);}
function doRun() {
    document.getElementById('msg').innerHTML = 'Processing JS...';
    setTimeout(function(){
         start = new Date();
         end = addSecs(start,5);
         do {start = new Date();} while (end-start > 0);
         document.getElementById('msg').innerHTML = 'Finished Processing';   
    },10);
}
$(function() {
    $('button').click(doRun);
});    
</script>
    </head>
<body>
    <div id="msg">Not Started</div>
    <button>jQuery</button>
    <a href="#" onclick="doRun()">javascript</a>
</body>
</html>

vous pouvez modifier le délai de setTimeout en fonction des besoins, il peut être nécessaire de l’augmenter pour les ordinateurs/navigateurs plus lents.

Modifier:

Vous pouvez également utiliser une alerte ou une boîte de dialogue de confirmation pour permettre à la page de se mettre à jour.

document.getElementById('msg').innerHTML = 'Processing JS...';
if ( confirm( "This task may take several seconds. Do you wish to continue?" ) ) {
     // run code here
}
18
Kevin B

Vous avez une boucle qui dure 5 secondes et bloque le navigateur Web pendant cette période. Puisque le navigateur Web est gelé, il ne peut effectuer aucun rendu. Vous devriez utiliser setTimeout() au lieu d'une boucle, mais je suppose que cette boucle n'est qu'un remplacement pour une fonction gourmande en ressources processeur qui prend un certain temps? Vous pouvez utiliser setTimeout pour permettre au navigateur d’obtenir un rendu avant d’exécuter votre fonction:

jQuery:

$(function() {
    $('button').click(function(){
        (function(cont){
            $('div').text('Processing JQ...');  
            start = new Date();
            end = addSecs(start,5);
            setTimeout(cont, 1);
        })(function(){
            do {start = new Date();} while (end-start > 0);
            $('div').text('Finished JQ');   
        })
    });
});

Vanilla JS:

document.getElementsByTagName('a')[0].onclick = function(){
    doRun(function(){
         do {start = new Date();} while (end-start > 0);
         document.getElementById('msg').innerHTML = 'Finished JS';   
    });
    return false;
};

function doRun(cont){
    document.getElementById('msg').innerHTML = 'Processing JS...';
    start = new Date();
    end = addSecs(start,5);
    setTimeout(cont, 1);
}

N'oubliez pas de toujours déclarer toutes les variables à l'aide du mot clé var et d'éviter de les exposer à la portée globale. Voici un JSFiddle:

http://jsfiddle.net/Paulpro/ypQ6m/

2
Paulpro

Je devais attendre que jQuery manipule le DOM puis transcrire les modifications (charger plusieurs fois les champs du formulaire dans un formulaire, puis le soumettre). Le DOM a grandi et l’insertion a pris de plus en plus de temps. J'ai sauvegardé les modifications en utilisant ajax pour obliger l'utilisateur à pouvoir continuer là où il s'était arrêté.

Cela n'a pas NE FONCTIONNE PAS comme prévu:

jQuery('.parentEl').prepend('<p>newContent</p>');
doSave(); // wrapping it into timeout was to short as well sometimes

Etant donné que les fonctions jQuery telles que .prepend() ne poursuivent la chaîne que lorsque cela est fait, voici ce qui semblait sembler faire l'affaire :

jQuery('.parentEl').prepend('<p>newContent</p>').queue(function() {
  doSave();
});
0
BananaAcid