【问题标题】:Calling a JavaScript function returned from an Ajax response调用从 Ajax 响应返回的 JavaScript 函数
【发布时间】:2021-03-03 00:31:49
【问题描述】:

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

这可能吗?

【问题讨论】:

    标签: javascript ajax function


    【解决方案1】:

    我认为可以在此表单下正确解释您的问题:“好的,我已经完成了所有 Ajax 的工作;我只想知道插入到 DIV 中的 JavaScript 函数是否可以随时调用在那一刻,我不想在上下文中调用它来回调“返回”。

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

    1) Ajax 回调返回的 JavaScript 代码在语法上必须是正确的;
    2) 即使您的函数声明插入到现有<div> 元素内的<script> 块中,浏览器也不会知道新函数的存在,因为声明代码从未执行过。因此,您必须 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 回调返回时函数的 contextual 调用,那么我的感觉是建议使用 Prototype 方法described by krosenvold,因为它是跨浏览器、经过测试且功能齐全的,这可以为您提供更好的未来实施路线图。

    【讨论】:

    • 这在示例中有效,因为所有内容都在同一页面中,并且时间不起作用。它不适用于实际利用 AJAX 方法调用外部文件进行处理的任何东西,因为 eval() 函数在外部进程有任何时间返回响应之前运行。
    • 精湛的答案真的节省了我的时间。上帝保佑你。
    • @Typel 在这里使用 eval() 有什么安全风险吗?
    • @Anupam 我认为普遍的共识是 eval() 总是存在与之相关的安全风险 - 我会注意它如何以及是否被使用。就个人而言,我不会将它与 AJAX 结合使用,以避免打开任何 XSS 漏洞。在大多数情况下,如果我需要从 AJAX 返回执行函数,我只需将参数传回,然后让函数的内容驻留在调用页面中并从调用页面执行(或类似的东西)。
    【解决方案2】:

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


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

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

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

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

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

    【讨论】:

    • 太棒了,一直在尝试解决这个问题 +1
    • 我已经尝试解决这个问题好几个小时了,才发现这个答案!这太棒了,应该被接受!谢谢,+1 :)
    • 实施此解决方案是否存在安全漏洞?
    • 2018 年仍然是现实:这听起来很危险,就像“默默无闻的安全”。如果我正在攻击您的网站并注意到 eval() 函数,那么我肯定会注意到那里的 ID 标签并修改我的攻击以将 malicios 代码插入到您的 &lt;script id="my-secure-id"&gt; 标签中。通过使用此解决方案,我没有看到任何额外的安全性。事实上,它只会给你虚假的安全感,更危险。但大约 6 年后,HTTPS 现在变得更加普遍。我相信对 eval() 的攻击不应该通过安全的 HTTPS 进行。假设我错了吗?
    • 显然 eval 在某些情况下是危险的,但是如果有人拦截你的代码,假设你没有在 ssl 上运行并将一些代码注入到 eval 中,他们有什么正当理由不能做同样的事情如果您的响应返回插入到 DOM 中的 HTML(他们可以添加一个脚本标签)。在我看来,对来自服务器的响应运行 eval 与服务器为您提供插入 DOM 的 HTML 具有相同的安全含义。不过也许有一些不同。
    【解决方案3】:

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

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

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

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

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

    这会检查函数是否存在。也许有人有更好的跨浏览器方式来完成 Internet Explorer 6 中的工作。

    【讨论】:

    • 这太棒了!谢谢!我一直在更新程序上使用“onSuccess”函数来手动评估 Javascript。但这更具可读性并且适用于所有浏览器。
    【解决方案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(),它非常适合我的目的。它的文档可以在here找到。

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

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

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

          【讨论】:

            【解决方案8】:

            做这种事情的清单:

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

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

            【讨论】:

              【解决方案9】:

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

              【讨论】:

                【解决方案10】:

                这段代码也可以,而是评估 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);
                }
                

                【讨论】:

                  【解决方案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 是 ajax 调用结果文本所在的(通常是 div)元素 ID。
                  url 是 ajax 调用 url。
                  busyMsg 将是目标元素中的临时文本。
                  当 ajax 事务成功完成时将调用finishCB
                  正如您在xhr.onreadystatechange = function() {...} 中看到的,所有&lt;script&gt; 元素都将从ajax 响应中收集并一一运行。它似乎对我很有效。最后两个参数是可选的。

                  【讨论】:

                    【解决方案12】:

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

                    【讨论】:

                      【解决方案13】:

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

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

                      不过,尽管如此,(我不明白您为什么要在 div 中插入脚本块?)即使是在脚本块中编写的内联脚本方法也可以访问。

                      【讨论】:

                      • 他不是问天气好不好,而是问是否可能
                      【解决方案14】:

                      我尝试了这里提供的所有技术,但最终可行的方法是将 JavaScript 函数放在应该发生的页面/文件中,并从 Ajax 的响应部分简单地作为函数调用它:

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

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

                      【讨论】:

                        【解决方案15】:

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

                        我有一个 AJAX 请求,它返回了一堆显示在叠加层中的 HTML。我需要将点击事件附加到返回的响应 HTML/覆盖中的按钮。在普通页面上,我会将我的 JavaScript 包装在“window.onload”或“$(document).ready”中,以便在呈现新叠加层的 DOM 后,它将事件处理程序附加到 DOM 对象,但是因为这是 AJAX 响应而不是新页面加载,所以该事件从未发生,浏览器从未执行我的 JavaScript,我的事件处理程序从未附加到 DOM 元素,我的新功能也不起作用。同样,我解决了我的“在 AJAX 响应中执行 JavaScript 问题”,方法是不在文档头部使用“$(document).ready”,而是将我的 JavaScript 放在文档末尾并让它在 HTML 之后运行/DOM 已被渲染。

                        【讨论】:

                          【解决方案16】:

                          如果您的 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";
                          ?>
                          

                          【讨论】:

                            【解决方案17】:

                            我需要做点什么,我发现这对我来说已经工作了很长时间,只是将其作为众多解决方案之一发布在这里,我喜欢没有 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;
                            }
                            

                            【讨论】:

                              【解决方案18】:

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

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

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

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 2019-11-20
                                • 1970-01-01
                                • 1970-01-01
                                • 2023-04-02
                                • 1970-01-01
                                • 1970-01-01
                                相关资源
                                最近更新 更多