调用从Ajax响应返回的JavaScript函数

时间:2009-02-04 10:18:22

标签: javascript ajax function

我有一个系统,我发送一个Ajax命令,它返回一个带有函数的脚本块。在DIV中正确插入此数据后,我希望能够调用此函数来执行所需的操作。

这可能吗?

18 个答案:

答案 0 :(得分:71)

我认为在这种形式下正确解释你的问题:“好的,我已经完成了所有Ajax的工作;我只是想知道我的Ajax回调插入到DIV中的JavaScript函数是否可以随时调用那个时刻,也就是说,我不想在回调返回“。

上调用它

好的,如果您的意思是这样,答案是肯定的,您可以在浏览器中的页面持久性期间随时调用您的新代码,在以下条件下:

1)Ajax回调返回的JavaScript代码必须语法正常;
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方法described by krosenvold,因为它是跨浏览器,经过测试并且功能齐全,这可以为您提供更好的未来实施路线图。

答案 1 :(得分:42)

  

注意:eval()很容易被滥用,假设请求被第三方拦截并向您发送不受信任的代码。然后使用eval()运行这个不受信任的代码。请参阅此处dangers of eval()


在返回的HTML / Ajax / JavaScript文件中,您将拥有一个JavaScript标记。给它一个ID,比如 runscript 。向这些标签添加id并不常见,但需要专门引用它。

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

在主窗口中,然后通过仅评估新的JavaScript代码块(在这种情况下,它被称为 runscript )来调用eval函数:

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

它起作用,至少在Internet Explorer 9和Google Chrome中是有效的。

答案 2 :(得分:9)

这是完全可能的,甚至还有一些相当合理的用例。使用Prototype框架完成如下操作。

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

请参阅Ajax更新程序的documentation。选项位于common options set。像往常一样,关于“这个”指向的地方有一些警告,所以请阅读细则。

将在加载时评估JavaScript代码。如果内容包含函数myFunc(), 你之后真的可以说myFunc()。可能如下。

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

检查函数是否存在。也许某人有更好的跨浏览器方式,可以在Internet Explorer 6中运行。

答案 3 :(得分:6)

对于您的代码来说,这似乎是一个相当奇怪的设计 - 通常直接从.js文件调用函数,然后只使用Ajax调用检索数据更有意义。

但是,我认为应该通过在响应上调用eval()来实现它 - 只要它是语法正确的JavaScript代码。

答案 4 :(得分:5)

使用jQuery,我会使用getScript

答案 5 :(得分:3)

我想补充一点,jQuery中有一个eval函数,允许你全局评估代码,这可以让你摆脱任何上下文问题。该函数名为globalEval(),它对我的​​目的很有用。其文档可以找到here

这是jQuery API文档提供的示例代码:

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

test();
// newVar === true

当您动态加载外部脚本时,此功能非常有用,您显然正在尝试这样做。

答案 6 :(得分:3)

请记住,如果你通过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 :(得分:2)

执行此类操作的清单:

  1. 返回的Ajax响应是eval(ed)。
  2. 函数以func_name = function() {...}
  3. 的形式声明

    更好的是,使用像Prototype一样处理它的框架。你有Ajax.updater

答案 8 :(得分:2)

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
                  }


        });
}

答案 9 :(得分:1)

这听起来不是一个好主意。

您应该从Ajax方法返回的数据中抽象出要包含在其余JavaScript代码中的函数。

但是,为了它的价值,(我不明白你为什么要在div中插入一个脚本块?)甚至可以访问在脚本块中编写的内联脚本方法。

答案 10 :(得分:1)

我常用的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)元素ID,其中ajax调用结果文本将会出现 url是ajax调用网址 busyMsg将成为目标元素中的临时文本 当ajax事务成功完成时,将调用finishCB 正如您在xhr.onreadystatechange = function() {...}中看到的那样,所有<script>元素都将从ajax响应中收集,并将逐个运行。它对我来说似乎很有效。最后两个参数是可选的。

答案 11 :(得分:0)

我测试了这个并且它有效。有什么问题?只需将新函数放入javascript元素中,然后调用它即可。它会起作用。

答案 12 :(得分:0)

我尝试了这里提供的所有技术,但最后的工作方式只是将JavaScript函数放在应该发生的页面/文件中,并将其作为函数从Ajax的响应部分调用:< / p>

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

这是第一次尝试,所以我决定分享。

答案 13 :(得分:0)

此代码也有效,而不是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);
}

答案 14 :(得分:0)

我今天通过将JavaScript放在响应HTML的底部来解决这个问题。

我有一个AJAX请求返回了一堆显示在叠加层中的HTML。我需要将单击事件附加到返回的响应HTML / overlay中的按钮。在普通页面上,我会将我的JavaScript包装在&#34; window.onload&#34;或&#34; $(文件).ready&#34;以便在渲染新叠加层的DOM之后将事件处理程序附加到DOM对象,但由于这是一个AJAX响应而不是新的页面加载,该事件从未发生过,浏览器从未执行过我的JavaScript,我的事件处理程序永远不会附加到DOM元素,我的新功能不起作用。再次,我解决了我在&#34;在AJAX响应问题中执行JavaScript的问题&#34;不使用&#34; $(文件).ready&#34;在文档的头部,但是将我的JavaScript放在文档的末尾,并在HTML / DOM呈现之后运行它。

答案 15 :(得分:0)

如果您的AJAX脚本运行时间超过几毫秒,则eval()将始终运行并在AJAX使用您尝试执行的脚本填充它之前评估空响应元素。

这里有一个非常简单的解决方法,应该适用于大多数情况并且可能更安全一些,而不是使用时间和eval()。使用eval()通常是不受欢迎的,因为被评估为代码的字符很容易被客户端操作。

概念

  1. 在主页面中包含您的javascript功能。编写它以便可以接受任何动态元素作为参数。
  2. 在您的AJAX文件中,使用官方DOM事件(onclick,onfocus,onblur,onload等)调用该函数。根据您的响应中的其他元素,您可以非常聪明地使其感觉无缝。将动态元素作为参数传递。
  3. 当您的响应元素被填充并且事件发生时,该函数将运行。
  4. 实施例

    在这个例子中,我想在将元素添加到页面之后,将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";
    ?>
    

答案 16 :(得分:0)

Federico Zancan's answer是正确的,但您不必为脚本提供ID并评估您的所有脚本。只需评估您的函数名称即可调用它。

为了在我们的项目中实现这一点,我们编写了一个代理函数来调用Ajax响应中返回的函数。

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

答案 17 :(得分:0)

我需要做些事情来做这件事,我发现这对我来说已经工作了很长时间,只是将其作为众多解决方案之一发布在这里,我喜欢没有 jQuery 的解决方案,以下功能可能对您有所帮助,您可以传递带有脚本标签的完整 html,它会解析并执行。

function parseScript(_source) {
    var source = _source;
    var scripts = new Array();

    // Strip out tags
    while(source.indexOf("<script") > -1 || source.indexOf("</script") > -1) {
        var s = source.indexOf("<script");
        var s_e = source.indexOf(">", s);
        var e = source.indexOf("</script", s);
        var e_e = source.indexOf(">", e);

        // Add to scripts array
        scripts.push(source.substring(s_e+1, e));
        // Strip from source
        source = source.substring(0, s) + source.substring(e_e+1);
        }

        // Loop through every script collected and eval it
        for(var i=0; i<scripts.length; i++) {
        try {
        if (scripts[i] != '')
        {
        try  {          //IE
            execScript(scripts[i]);
        }
        catch(ex)           //Firefox
        {
            window.eval(scripts[i]);
        }

        }
        }
        catch(e) {
           // do what you want here when a script fails
           if (e instanceof SyntaxError) console.log (e.message+' - '+scripts[i]);
        }
        }
        // Return the cleaned source
        return source;
}