Вызов функции JavaScript, возвращенной из ответа Ajax


У меня есть система, в которой я отправляю команду Ajax, которая возвращает блок скрипта с функцией в нем. После того, как эти данные правильно вставлены в DIV, я хочу иметь возможность вызывать эту функцию для выполнения необходимых действий.

это возможно?

17 62

17 ответов:

Я думаю, чтобы правильно интерпретировать ваш вопрос в этой форме:"хорошо, я уже закончил со всеми вещами Ajax; я просто хочу знать, если функция JavaScript мой Ajax обратный вызов вставлен в DIV вызывается в любое время с этого момента, то есть, я не хочу, чтобы вызвать его контекстуально к обратному вызову".

ОК, если вы имеете в виду что-то вроде этого ответ да, вы можете вызвать свой новый код к этому моменту в любое время во время сохранения страницы в браузере, при следующих обстоятельствах:

1) Ваш код JavaScript, возвращаемый Ajax callback, должен быть синтаксически ОК;
2) Даже если объявление функции вставлено в <script> блок в существующей <div> элемент, браузер не будет знать, что новая функция существует, так как код объявления никогда не выполнялся. Итак, вы должны eval() ваш код объявления, возвращенный обратным вызовом Ajax, чтобы эффективно объявить вашу новую функцию и иметь ее доступной во время целая страница жизни.

даже если совсем пустышка, этот код объясняет идею:

<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>

Я не использовал Ajax, но концепция та же (даже если пример, который я выбрал, конечно, не очень умный :-)

вообще говоря, я не ставлю под сомнение ваш дизайн решения, т. е. более или менее целесообразно ли экстернализировать + обобщить функцию в отдельном .JS file и тому подобное, но обратите внимание, что такое решение может вызвать дальнейшие проблемы, особенно если ваши вызовы Ajax должны повторяться, т. е. если контекст одной и той же функции должен измениться или в случае сохранения объявленной функции, возможно, вам следует серьезно рассмотреть возможность изменения вашего дизайна на один из предложенных примеров в этом потоке.

наконец, если я неправильно понял ваш вопрос, А вы про контекстная вызов функции, когда возвращается ваш обратный вызов Ajax, то мое чувство состоит в том, чтобы предложить подход прототипа описал krosenvold, поскольку это кросс-браузер, протестированный и полностью функциональный, и это может дать вам лучшую дорожную карту для будущих реализаций.

Примечание: eval () можно легко злоупотреблять, скажем, что запрос перехватывается третьей стороной и отправляет вам не доверенный код. Тогда с eval () вы будете запускать этот не доверенный код. См. здесь опасности eval ().


внутри возвращенного файла HTML / Ajax / JavaScript у вас будет тег JavaScript. Дайте ему идентификатор, например runscript. Это необычно, чтобы добавить идентификатор к этим тегам, но это нужно было ссылаться на него конкретно.

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

в главном окне вызовите функцию eval, оценив только этот новый блок кода JavaScript (в данном случае он называется runscript):

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

и это работает, по крайней мере, в Internet Explorer 9 и Google Chrome.

это вполне возможно, и есть даже некоторые вполне законные варианты использования для этого. Используя прототип основа это делается следующим образом.

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

посмотреть документация из Ajax updater. Параметры находятся в настройки. Как обычно, есть некоторые предостережения о том, куда указывает "это", поэтому прочитайте мелкий шрифт.

код JavaScript будет оцениваться при загрузке. Если содержимое содержит функцию myFunc(), вы могли бы просто сказать myFunc() потом. Может быть, следующим образом.

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

это проверяет, если функция не существует. Возможно, у кого-то есть лучший кросс-браузерный способ сделать то, что работает в Internet Explorer 6.

Это кажется довольно странным дизайном для вашего кода-обычно имеет смысл, чтобы ваши функции вызывались непосредственно из a .JS-файл, а затем только извлекать данные с помощью вызова Ajax.

однако, я считаю, что он должен работать, позвонив eval () на ответ-при условии, что это синтаксически правильный код JavaScript.

с jQuery я бы сделал это с помощью getScript

просто помните, если вы создаете функцию, как показано ниже через ajax...

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

...и выполнить его через eval, вы, вероятно, получите проблему контекста. Возьмите это в качестве функции обратного вызова:

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

вы будете объявлять функцию внутри функции,поэтому эта новая функция будет доступна только в этой области.

Если вы хотите создать глобальную функцию в этом случае, можно объявить так:

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

но, я тоже думаю ты не должен этого делать...

извините за любую ошибку здесь...

Я хотел бы добавить, что в jQuery есть функция eval, позволяющая вам оценивать код глобально, что должно избавить вас от любых контекстуальных проблем. Функция называется globalEval() и он работал большой для моих целей. Документацию можно найти здесь.

Это пример кода, предоставленный документацией API jQuery:

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

test();
// newVar === true

эта функция чрезвычайно полезна, когда речь заходит о загрузке внешних скриптов динамически, что вы, по-видимому, пытались сделать.

контрольный список для выполнения такой вещи:

  1. возвращенный ответ Ajax-eval (ed).
  2. функции объявляются в форме func_name = function() {...}

еще лучше, используйте фреймворки, которые обрабатывают его, как в прототип. У вас есть Ajax.updater.

код на PHP Имя класса файла.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']);

Теперь с вашей Html-страницы вы должны вызвать функцию ajax для отправки данных.

....  <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()"/>

Теперь в нашем локальном скрипте.js определим 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
                  }


        });
}

Это не звучит как хорошая идея.

вы должны абстрагировать функцию для включения в остальную часть вашего кода JavaScript из данных, возвращаемых методами Ajax.

для чего это стоит, хотя, (и я не понимаю, почему вы вставляете блок скрипта в div?) даже встроенные методы скрипта, написанные в блоке скрипта, будут доступны.

моя обычная функция вызова ajax:

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;
}

объяснение:
targetId - это идентификатор элемента (обычно div), куда будет отправлен текст результата вызова ajax.
url - это url-адрес вызова ajax.
busyMsg будет временный текст в целевом элементе.
finishCB будет вызван после успешного завершения транзакции ajax.
Как вы видите в xhr.onreadystatechange = function() {...} все <script> элементы будут собраны из ответа ajax и будут выполняться один за другим. Это, кажется, работает очень хорошо для меня. Два последних параметра являются необязательными.

Я проверил это, и это работает. В чем проблема? Просто поместите новую функцию в свой элемент javascript, а затем вызовите ее. Это сработает.

я попробовал все методы, предлагаемые здесь, но, наконец, способ, который работал, состоял в том, чтобы просто поместить функцию JavaScript внутри страницы / файла, где она должна произойти, и вызвать ее из ответной части Ajax просто как функцию:

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

это сработало с первой попытки, поэтому я решил поделиться.

этот код также работает, вместо того, чтобы eval html я собираюсь добавить скрипт в голову

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);
}

Я решил это сегодня, поставив свой JavaScript в нижней части ответа HTML.

У меня был запрос AJAX, который вернул кучу HTML, которая отображалась в наложении. Мне нужно было прикрепить событие click к кнопке в возвращенном ответе HTML / overlay. На обычной странице Я бы обернул свой JavaScript в " окно.события onload" или "$(документ).ready", чтобы он прикрепил обработчик событий к объекту DOM после того, как DOM для нового наложения был визуализирован, но потому что это был ответ AJAX, а не новая загрузка страницы, это событие никогда не происходило, браузер никогда не выполнял мой JavaScript, мой обработчик событий никогда не привязывался к элементу DOM, и моя новая часть функциональности не работала. Опять же, я решил свою "выполнение JavaScript в проблеме ответа AJAX", не используя "$(document).готов " в голове документа, но поместив мой JavaScript в конец документа и запустив его после того, как HTML/DOM был отрисован.

Если ваш сценарий AJAX занимает более нескольких миллисекунд для запуска, eval () всегда будет работать вперед и оценивать пустой элемент ответа, прежде чем AJAX заполнит его сценарием, который вы пытаетесь выполнить.

вместо того, чтобы возиться со временем и eval(), вот довольно простой обходной путь, который должен работать в большинстве ситуаций и, вероятно, немного более безопасным. Использование eval () обычно не одобряется, потому что символы, оцениваемые как код, можно легко манипулировать клиентский.

концепция

  1. включите функцию javascript на главной странице. Напишите его так, чтобы любые динамические элементы могли быть приняты в качестве аргументов.
  2. в вашем AJAX-файле вызовите функцию, используя официальное событие DOM (onclick, onfocus, onblur, onload и т. д.) В зависимости от того, какие другие элементы находятся в вашем ответе, вы можете получить довольно умный о том, чтобы сделать его чувствовать себя бесшовным. Передайте свои динамические элементы в качестве аргументов.
  3. когда ваш элемент ответа заполняется и происходит событие, функция запускается.

пример

начать.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";
?>

Федерико Zancan это правильно, но вам не нужно давать вашему скрипту идентификатор и оценивать весь ваш скрипт. Просто оцените свое имя функции, и его можно вызвать.

чтобы достичь этого в нашем проекте, мы написали прокси-функцию для вызова функции, возвращаемой внутри ответа Ajax.

function FunctionProxy(functionName){
    var func = eval(functionName);
    func();
}