web-dev-qa-db-fra.com

Appeler une fonction JavaScript renvoyée par une réponse Ajax

J'ai un système où j'envoie une commande Ajax, qui renvoie un bloc de script contenant une fonction. Une fois que ces données sont correctement insérées dans la DIV, je veux pouvoir appeler cette fonction pour effectuer les actions requises.

Est-ce possible?

63
williamtroup

Je pense que pour interpréter correctement votre question sous cette forme: "OK, j’en ai déjà terminé avec tout ce qui concerne Ajax; je souhaite simplement savoir si la fonction JavaScript que mon rappel Ajax inséré dans la DIV est appelable à tout moment à partir de ce moment , c’est-à-dire que je ne veux pas l’appeler de manière contextuelle au retour de rappel ".

OK, si vous voulez dire quelque chose comme ceci, la réponse est oui, vous pouvez appeler votre nouveau code à ce moment-là, à tout moment pendant la persistance de la page dans le navigateur, dans les conditions suivantes:

1) Votre code JavaScript renvoyé par le rappel Ajax doit être syntaxiquement correct;
2) Même si votre déclaration de fonction est insérée dans un bloc <script> au sein d'un élément <div> existant, le navigateur ne saura pas que la nouvelle fonction existe, car le code de déclaration n'a jamais été exécuté. Donc, vous devez eval() votre code de déclaration renvoyé par le rappel Ajax, afin de déclarer efficacement votre nouvelle fonction et de la garder disponible pendant toute la durée de vie de la page.

Même s'il est assez factice, ce code explique l'idée:

<html>
    <body>
        <div id="div1">
        </div>
        <div id="div2">
            <input type="button" value="Go!" onclick="go()" />
        </div>
        <script type="text/javascript">
            var newsc = '<script id="sc1" type="text/javascript">function go() { alert("GO!") }<\/script>';
            var e = document.getElementById('div1');
            e.innerHTML = newsc;
            eval(document.getElementById('sc1').innerHTML);
        </script>
    </body>
</html>

Je n'ai pas utilisé Ajax, mais le concept est le même (même si l'exemple que j'ai choisi n'est certainement pas très intelligent :-)

De manière générale, je ne remets pas en question la conception de votre solution, c’est-à-dire s’il est plus ou moins approprié d’externaliser + de généraliser la fonction dans un fichier .js séparé, etc. vos invocations Ajax devraient se répéter, c'est-à-dire si le contexte de la même fonction devait changer ou si la persistance de la fonction déclarée devait être concernée, alors vous devriez peut-être envisager sérieusement de changer votre conception en l'un des exemples suggérés dans ce fil.

Enfin, si j'ai mal compris votre question et que vous parlez de l'invocation contextuelle de la fonction lorsque votre rappel Ajax est renvoyé, mon sentiment est de suggérer l'approche Prototype décrite par krosenvold , car elle est croisée. -browser, testé et entièrement fonctionnel, ce qui peut vous donner une meilleure feuille de route pour les mises en œuvre futures.

71
Federico Zancan

Note: eval () peut être facilement utilisé à mauvais escient, disons que la requête est interceptée par un tiers et vous envoie un code non fiable. Ensuite, avec eval (), vous exécuteriez ce code non fiable. Reportez-vous ici aux dangers de eval () .


Dans le fichier HTML/Ajax/JavaScript renvoyé, vous aurez une balise JavaScript. Donnez-lui un identifiant, comme runcript. Il est rare d'ajouter un identifiant à ces balises, mais il est nécessaire de le référencer spécifiquement.

<script type="text/javascript" id="runscript">
    alert("running from main");
</script>

Dans la fenêtre principale, appelez la fonction eval en évaluant uniquement ce NOUVEAU bloc de code JavaScript (dans ce cas, il s'appelle runscript):

eval(document.getElementById("runscript").innerHTML);

Et cela fonctionne, au moins dans Internet Explorer 9 et Google Chrome.

40
Dandymon

C'est tout à fait possible, et il existe même des cas d'utilisation assez légitimes pour cela. En utilisant le framework Prototype , procédez comme suit.

new Ajax.Updater('items', '/items.url', {
    parameters: { evalJS: true}
});

Voir documentation du programme de mise à jour Ajax. Les options sont dans le jeu d'options common . Comme d'habitude, il y a des réserves quant à l'endroit où "ceci" pointe, alors lisez les petits caractères.

Le code JavaScript sera évalué lors du chargement. Si le contenu contient la fonction myFunc(),vous pouvez simplement dire myFunc() par la suite. Peut-être comme suit.

if (window["myFunc"])
   myFunc()

Ceci vérifie si la fonction existe. Peut-être que quelqu'un a un meilleur moyen de le faire avec d'autres navigateurs que dans Internet Explorer 6.

9
krosenvold

Cela semble plutôt étrange pour votre code - il est généralement plus logique d’appeler vos fonctions directement à partir d’un fichier .js, puis de ne récupérer les données qu’avec l’appel Ajax.

Cependant, je pense que cela devrait fonctionner en appelant eval () sur la réponse - à condition que le code JavaScript soit syntaxiquement correct.

6
DanSingerman

Avec jQuery, je le ferais avec getScript

5
kgiannakakis

Rappelez-vous simplement si vous créez une fonction comme ci-dessous via ajax ...

function foo()
{
    console.log('foo');
}

... et exécutez-le via eval, vous aurez probablement un problème de contexte . Prenez ceci comme fonction de rappel

function callback(result)
{
    responseDiv = document.getElementById('responseDiv');
    responseDiv.innerHTML = result;
    scripts = responseDiv.getElementsByTagName('script');
    eval(scripts[0]);
}

Vous allez déclarer une fonction dans une fonction, de sorte que cette nouvelle fonction ne sera accessible que sur cette étendue.

Si vous souhaitez créer une fonction globale dans ce scénario, vous pouvez le déclarer comme suit:

window.foo = function ()
{
    console.log('foo');
};

Mais, je pense aussi que tu ne devrais pas faire ça ...

Désolé pour toute erreur ici ...

3
Daniel Hartmann

J'aimerais ajouter qu'il existe une fonction eval dans jQuery vous permettant d'évaluer le code globalement, ce qui devrait vous débarrasser de tout problème contextuel. La fonction s'appelle globalEval () et fonctionne très bien pour mes besoins. Sa documentation peut être trouvée ici .

Voici l'exemple de code fourni par la documentation de l'API jQuery:

function test()
{
  jQuery.globalEval("var newVar = true;")
}

test();
// newVar === true

Cette fonction est extrêmement utile pour charger dynamiquement des scripts externes, ce que vous tentiez apparemment de faire.

3
beta

Code latéral PHPNom du fichier class.sendCode.php

<?php
class  sendCode{ 

function __construct($dateini,$datefin) {

            echo $this->printCode($dateini,$datefin);
        }

    function printCode($dateini,$datefin){

        $code =" alert ('code Coming from AJAX {$this->dateini} and {$this->datefin}');";
//Insert all the code you want to execute, 
//only javascript or Jquery code , dont incluce <script> tags
            return $code ;
    }
}
new sendCode($_POST['dateini'],$_POST['datefin']);

Maintenant, depuis votre page HTML, vous devez déclencher la fonction ajax pour envoyer les données.

....  <script src="http://code.jquery.com/jquery-1.9.1.js"></script> ....
Date begin: <input type="text" id="startdate"><br>
Date end : <input type="text" id="enddate"><br>
<input type="button" value="validate'" onclick="triggerAjax()"/>

Maintenant, dans notre script.js local, nous allons définir le ajax

function triggerAjax() {
    $.ajax({
            type: "POST",
            url: 'class.sendCode.php',
            dataType: "HTML",
            data : {

                dateini : $('#startdate').val(),
                datefin : $('#enddate').val()},

                  success: function(data){
                      $.globalEval(data);
// here is where the magic is made by executing the data that comes from
// the php class.  That is our javascript code to be executed
                  }


        });
}
2
Sultanos

Une liste de contrôle pour faire une telle chose:

  1. la réponse Ajax retournée est eval (ed).
  2. les fonctions sont déclarées sous la forme func_name = function() {...}

Mieux encore, utilisez des frameworks qui le traitent comme dans Prototype . Vous avez Ajax.updater.

2
Nasaralla

Ma fonction d'appel ajax habituelle:

function xhr_new(targetId, url, busyMsg, finishCB)
{
    var xhr;

    if(busyMsg !== undefined)
        document.getElementById(targetId).innerHTML = busyMsg;

    try { xhr = new ActiveXObject('Msxml2.XMLHTTP'); }
    catch(e)
    {
        try { xhr = new ActiveXObject('Microsoft.XMLHTTP'); }
        catch(e2)
        {
            try { xhr = new XMLHttpRequest(); }
            catch(e3) { xhr = false; }
        }
    }

    xhr.onreadystatechange = function()
    {
        if(xhr.readyState == 4)
        {
            if(xhr.status == 200)
            {
                var target = document.getElementById(targetId)
                target.innerHTML = xhr.responseText;
                var scriptElements = target.getElementsByTagName("script");
                var i;
                for(i = 0; i < scriptElements.length; i++)
                    eval(scriptElements[i].innerHTML);
                if(finishCB !== undefined)
                    finishCB();
            }
            else
                document.getElementById(targetId).innerHTML = 'Error code: ' + xhr.status;
        }
    };

    xhr.open('GET', url, true);
    xhr.send(null);
    // return xhr;
}

Quelques explications:
targetId est un ID d'élément (généralement div) vers lequel ira le texte du résultat de l'appel ajax.
url est l'URL de l'appel ajax.
busyMsg sera le texte temporaire dans l'élément cible.
finishCB sera appelée lorsque la transaction ajax sera terminée avec succès.
Comme vous le voyez dans xhr.onreadystatechange = function() {...}, tous les éléments <script> seront collectés à partir de la réponse ajax et seront exécutés un par un. Cela semble très bien fonctionner pour moi. Les deux derniers paramètres sont facultatifs.

1
Ray

Cela ne semble pas être une bonne idée. 

Vous devez résumer la fonction à inclure dans le reste de votre code JavaScript à partir des données renvoyées par les méthodes Ajax.

Pour ce que cela vaut, cependant (et je ne comprends pas pourquoi vous insérez un bloc de script dans un div?), Même les méthodes de script intégrées dans un bloc de script seront accessibles.

1
annakata

Ce code fonctionne également, au lieu d’évaluer le code HTML, je vais ajouter le script à la tête

function RunJS(objID) {
//alert(http_request.responseText);
var c="";
var ob = document.getElementById(objID).getElementsByTagName("script");
for (var i=0; i < ob.length - 1; i++) {
    if (ob[i + 1].text != null) 
       c+=ob[i + 1].text;
}
var s = document.createElement("script");
s.type = "text/javascript";
s.text = c;
document.getElementsByTagName("head")[0].appendChild(s);
}
0
user2054758

Si votre script AJAX nécessite plus de quelques millisecondes, eval () s'exécutera toujours et évaluera l'élément de réponse vide avant que AJAX ne le remplisse avec le script que vous essayez d'exécuter.

Plutôt que de perdre du temps avec timing et eval (), voici une solution de contournement assez simple qui devrait fonctionner dans la plupart des situations et qui est probablement un peu plus sûre. L'utilisation de eval () est généralement mal vue parce que les caractères évalués en tant que code peuvent facilement être manipulés côté client.

Concept

  1. Incluez votre fonction javascript dans la page principale. Ecrivez-le pour que tous les éléments dynamiques puissent être acceptés comme arguments.
  2. Dans votre fichier AJAX, appelez la fonction en utilisant un événement DOM officiel (onclick, onfocus, onblur, onload, etc.) En fonction des autres éléments de votre réponse, vous pouvez être assez intelligent pour la rendre transparente. . Passez vos éléments dynamiques en argument.
  3. Lorsque votre élément de réponse est rempli et que l'événement a lieu, la fonction est exécutée.

Exemple

Dans cet exemple, je souhaite attacher une liste dynamique de saisie semi-automatique de la bibliothèque jquery-ui à un élément AJAX APRÈS que l'élément ait été ajouté à la page. Facile, non?

start.php

<!DOCTYPE html>
<html>
<head>
<title>Demo</title>
<!-- these libraries are for the autocomplete() function -->
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/ui-lightness/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<script type="text/javascript">
<!--
// this is the ajax call
function editDemoText(ElementID,initialValue) {
    try { ajaxRequest = new XMLHttpRequest();
    } catch (e) {
    try { ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
    try { ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
    } catch (e) {
    return false;
    }}}
    ajaxRequest.onreadystatechange = function() {
        if ( ajaxRequest.readyState == 4 ) {
            var ajaxDisplay = document.getElementById('responseDiv');
            ajaxDisplay.innerHTML = ajaxRequest.responseText;
            }
        }
    var queryString = "?ElementID="+ElementID+"&initialValue="+initialValue;
    ajaxRequest.open("GET", "ajaxRequest.php"+queryString, true);
    ajaxRequest.send(null);
    }

// this is the function we wanted to call in AJAX, 
// but we put it here instead with an argument (ElementID)
function AttachAutocomplete(ElementID) {
    // this list is static, but can easily be pulled in from 
    // a database using PHP. That would look something like this:
    /*
     * $list = "";
     * $r = mysqli_query($mysqli_link, "SELECT element FROM table");
     * while ( $row = mysqli_fetch_array($r) ) {
     *    $list .= "\".str_replace('"','\"',$row['element'])."\",";
     *    }
     * $list = rtrim($list,",");
     */
    var availableIDs = ["Demo1","Demo2","Demo3","Demo4"];
    $("#"+ElementID).autocomplete({ source: availableIDs });
    }
//-->
</script>
</head>
<body>
<!-- this is where the AJAX response sneaks in after DOM is loaded -->
<!-- we're using an onclick event to trigger the initial AJAX call -->
<div id="responseDiv"><a href="javascript:void(0);" onclick="editDemoText('EditableText','I am editable!');">I am editable!</a></div>
</body>
</html>

ajaxRequest.php

<?php
// for this application, onfocus works well because we wouldn't really 
// need the autocomplete populated until the user begins typing
echo "<input type=\"text\" id=\"".$_GET['ElementID']."\" onfocus=\"AttachAutocomplete('".$_GET['ElementID']."');\" value=\"".$_GET['initialValue']."\" />\n";
?>
0
Typel

J'ai testé cela et ça marche. Quel est le problème? Insérez simplement la nouvelle fonction dans votre élément javascript, puis appelez-la. Ça va marcher.

0
user875234

La réponse de Federico Zancan est correcte, mais vous n'avez pas à donner à votre script un ID et à évaluer tout votre script. Il suffit d’évaluer le nom de votre fonction et elle peut être appelée.

Pour y parvenir dans notre projet, nous avons écrit une fonction proxy pour appeler la fonction retournée dans la réponse Ajax.

function FunctionProxy(functionName){
    var func = eval(functionName);
    func();
}
0
Hüseyin Yağlı

J'ai essayé toutes les techniques proposées ici mais finalement, la méthode qui a fonctionné a consisté simplement à placer la fonction JavaScript à l'intérieur de la page/du fichier où cela devait se produire et à l'appeler à partir de la partie réponse de Ajax simplement en tant que fonction:

...
}, function(data) {
    afterOrder();
}

Cela a fonctionné à la première tentative, alors j'ai décidé de partager.

0
Sagive SEO

J'ai résolu ce problème aujourd'hui en plaçant mon code JavaScript au bas de la réponse HTML.

J'avais une demande AJAX qui renvoyait un groupe de HTML qui était affiché dans une superposition. J'avais besoin d'attacher un événement de clic à un bouton dans la réponse HTML/superposition renvoyée. Sur une page normale, j’enveloppais mon JavaScript dans un "window.onload" ou un "$ (document) .ready" afin qu’il attache le gestionnaire d’événements à l’objet DOM après le rendu du DOM pour la nouvelle superposition. parce que c'était une réponse AJAX et non un nouveau chargement de page, cet événement ne s'est jamais produit, le navigateur n'a jamais exécuté mon code JavaScript, mon gestionnaire d'événements ne s'est jamais attaché à l'élément DOM et ma nouvelle fonctionnalité n'a pas fonctionné. . De nouveau, j’ai résolu mon «problème d’exécution de JavaScript dans un problème de réponse AJAX» en n’utilisant pas «$ (document) .ready» dans la tête du document, mais en plaçant mon code JavaScript à la fin du document et en l’ayant exécuté après que le HTML/DOM ait été rendu.

0
Andrew Koper