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

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

Возможно ли это?

Ответ 1

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

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

1) Код JavaScript, возвращаемый обратным вызовом Ajax, должен быть синтаксически ОК,
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 и т.п., но, пожалуйста, обратите внимание, что такое решение может вызвать дополнительные проблемы, особенно если ваши призывы Ajax должны повторяться, т.е. если контекст одной и той же функции должен измениться или в случае, если проблема с заявленной функцией должна быть затронута, возможно, вам стоит серьезно подумать о том, чтобы изменить дизайн на один из предлагаемых примеров в этом потоке.

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

Ответ 2

Ни один из них не работал у меня

Следующее делает!

Мне понадобилось несколько часов, чтобы найти обходной путь.

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

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

В главном окне вызовите функцию eval, оценив только этот НОВЫЙ блок кода JavaScript (в этом случае он называется runcript):

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

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

Ответ 3

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

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

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

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

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

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

Просто запомните, если вы создаете функцию следующим способом через 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');
};

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

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

Ответ 7

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

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

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

test();
// newVar === true

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

Ответ 8

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

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

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

Ответ 9

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

Теперь со страницы 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()"/>

Теперь на нашем локальном script.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
                  }


        });
}

Ответ 10

Это не похоже на хорошую идею.

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

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

Ответ 11

Моя обычная функция вызова 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 - это адрес вызова ajax.
busyMsg будет временным текстом в целевом элементе.
finishCB будет вызываться, когда операция ajax завершена успешно.
Как вы видите в xhr.onreadystatechange = function() {...} все элементы <script> будут собраны из ответа ajax и будут запускаться один за другим. Кажется, он работает очень хорошо для меня. Два последних параметра являются необязательными.

Ответ 12

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

Ответ 13

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

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

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

Ответ 14

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

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

Ответ 15

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

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

Ответ 16

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

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

Концепция

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

Пример

В этом примере я хочу добавить динамический список автозаполнения из библиотеки jquery-ui в элемент AJAX ПОСЛЕ того, как элемент был добавлен на страницу. Легко, правильно?

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";
?>

Ответ 17

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

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

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