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


68

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

这可能吗?

Answers:


73

我想以这种形式正确解释您的问题:“好的,我已经用完所有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回调返回时该函数的上下文调用,那么我的感觉是建议由krosenvold描述的Prototype方法,因为它是跨浏览器,经过测试且功能齐全的,这可以为您提供更好的未来实施路线图。


1
在示例中这是可行的,因为所有内容都在同一页面中,并且计时不起作用。它与实际利用AJAX方法调用外部文件进行处理的任何事情都不起作用,因为eval()函数在外部进程有任何时间返回响应之前运行。
Typel

出色的答案确实节省了我的时间。上帝祝福你。
Manish

@Typel在此处使用eval()有安全隐患吗?
Anupam

1
@Anupam我认为,普遍共识是eval()始终存在与之相关的安全风险-我会很小心如何以及是否使用它。就个人而言,我不会将其与AJAX结合使用以避免打开任何XSS漏洞。在大多数情况下,如果我需要一个函数来从AJAX返回执行,我只是将参数传递回去,而让函数的内容驻留并从调用页面执行(或类似操作)。
Typel

41

注意:eval()很容易被滥用,可以说该请求被第三方拦截并向您发送了不可信的代码。然后,使用eval()将运行此不受信任的代码。有关eval()危险,请参见此处。


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

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

在主窗口中,然后通过仅评估该JavaScript代码的NEW块(在本例中为runscript)来调用eval函数:

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

它至少在Internet Explorer 9和Google Chrome中有效。



我已经尝试解决了几个小时,才遇到这个答案!这太棒了,应该可以接受!谢谢,+ 1 :)
布莱恩·兹维克

4
实施此解决方案是否存在安全漏洞?
Savas Vedova

在2018年仍然是实际的:听起来像是“默默无闻的安全”。如果我正在攻击您的网站并注意到eval()函数,那么我肯定会注意到那里的ID标签,并修改我的攻击以仅将malicios代码插入您的<script id="my-secure-id">标签中。通过使用此解决方案,看不到任何增加的安全性。实际上,它只会给您带来错误的安全感,这更加危险。但是大约6年后,HTTPS现在变得非常普遍。我相信通过安全的HTTPS不可能对eval()进行攻击。我认为那是错误的吗?
JanBradáč18年

显然,在某些情况下eval是危险的,但是如果有人假设您没有在ssl上运行并将其插入到eval中来拦截您的代码,那么是否有任何合理的原因,如果您的响应返回插入的HTML,他们将无法执行相同的操作进入DOM(他们可以只向其中添加脚本标签)。在我看来,在服务器的响应上运行eval具有与服务器相同的安全隐患,该服务器为您提供插入DOM的HTML。也许有些不同。
Joel M

9

这是完全可能的,甚至有一些相当合法的用例。使用原型框架,其操作如下。

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

请参阅Ajax更新程序的文档。这些选项在通用选项集中。像往常一样,关于“此”指向何处有一些警告,因此请阅读精美的印刷品。

JavaScript代码将在加载时进行评估。如果内容包含功能,myFunc(), 您实际上可以myFunc()稍后再说。可能如下。

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

这将检查该功能是否存在。也许有人有更好的跨浏览器方式可以在Internet Explorer 6中运行。


好厉害!谢谢!我一直在更新程序上使用“ onSuccess”功能来手动评估Javascript。但这更具可读性,并且可以在所有浏览器中使用。
Matthias Kleine

6

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

但是,我相信它应该通过在响应上调用eval()来工作-只要它在语法上是正确的JavaScript代码。



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

但是,我也认为您不应该这样做...

抱歉在这里有任何错误...


3

我想补充一点,jQuery中有一个eval函数,允许您全局评估代码,这将使您摆脱任何上下文问题。该函数称为globalEval(),它对我来说非常有用。它的文档可以在这里找到。

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

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

test();
// newVar === true

当动态加载您显然试图执行的外部脚本时,此功能非常有用。


2

做这样的事情的清单:

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

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


2

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

现在在本地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
                  }


        });
}

1

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

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

但是,就其价值而言(而且我不明白为什么要在div中插入脚本块?),甚至可以访问用脚本块编写的内联脚本方法。


1

此代码也可以正常工作,而不是评估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);
}

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调用结果文本将在该元素ID中使用。
url是ajax调用网址。
busyMsg将是目标元素中的临时文本。
finishCBajax事务成功完成时将调用。
如您所见,xhr.onreadystatechange = function() {...}所有<script>元素都将从ajax响应中收集并逐一运行。它对我来说似乎很好用。最后两个参数是可选的。


0

我已经对此进行了测试,并且可以正常工作。有什么问题?只需将新函数放入您的javascript元素中,然后调用它即可。它将起作用。


0

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

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

第一次尝试成功,所以我决定分享。


0

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

我有一个AJAX请求,该请求返回了一堆显示在叠加层中的HTML。我需要将click事件附加到返回的响应HTML /叠加层中的按钮上。在普通页面上,我将JavaScript封装在“ window.onload”或“ $(document).ready”中,以便在呈现新叠加层的DOM之后将事件处理程序附加到DOM对象,但是因为这是AJAX响应而不是新的页面加载,所以该事件从未发生,浏览器从未执行过我的JavaScript,事件处理程序也从未附加到DOM元素上,而我的新功能却无法正常工作。再次,通过不使用文档开头的“ $(document).ready”,解决了“在AJAX响应问题中执行JavaScript”的问题,


0

如果您的AJAX脚本要花费几毫秒的时间才能运行,则eval()将始终运行并评估空响应元素,然后AJAX会使用您要执行的脚本填充该响应元素。

这不是一个浪费时间和eval()的方法,而是一种非常简单的解决方法,该方法应可在大多数情况下使用,并且可能更安全。通常不赞成使用eval(),因为被评估为代码的字符可以很容易地在客户端进行操作。

概念

  1. 在首页中包含您的JavaScript函数。编写它,以便任何动态元素都可以作为参数接受。
  2. 在您的AJAX文件中,通过使用正式的DOM事件(onclick,onfocus,onblur,onload等)来调用该函数。根据响应中包含的其他元素,您可以非常巧妙地使其无缝。将动态元素作为参数传递。
  3. 当您的响应元素被填充并且事件发生时,该函数运行。

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

-1

Federico Zancan的答案是正确的,但是您不必给脚本一个ID并评估所有脚本。只需评估您的函数名称,就可以调用它。

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

function FunctionProxy(functionName){
    var func = eval(functionName);
    func();
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.