【发布时间】:2021-03-03 00:31:49
【问题描述】:
我有一个发送 Ajax 命令的系统,该命令返回一个脚本块,其中包含一个函数。将此数据正确插入 DIV 后,我希望能够调用此函数来执行所需的操作。
这可能吗?
【问题讨论】:
标签: javascript ajax function
我有一个发送 Ajax 命令的系统,该命令返回一个脚本块,其中包含一个函数。将此数据正确插入 DIV 后,我希望能够调用此函数来执行所需的操作。
这可能吗?
【问题讨论】:
标签: javascript ajax function
我认为可以在此表单下正确解释您的问题:“好的,我已经完成了所有 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,因为它是跨浏览器、经过测试且功能齐全的,这可以为您提供更好的未来实施路线图。
【讨论】:
注意: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 中有效。
【讨论】:
<script id="my-secure-id"> 标签中。通过使用此解决方案,我没有看到任何额外的安全性。事实上,它只会给你虚假的安全感,更危险。但大约 6 年后,HTTPS 现在变得更加普遍。我相信对 eval() 的攻击不应该通过安全的 HTTPS 进行。假设我错了吗?
这是完全可能的,甚至有一些相当合理的用例。使用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 中的工作。
【讨论】:
这对于您的代码来说似乎是一个相当奇怪的设计 - 通常让您的函数直接从 .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(),它非常适合我的目的。它的文档可以在here找到。
这是 jQuery API 文档提供的示例代码:
function test()
{
jQuery.globalEval("var newVar = true;")
}
test();
// newVar === true
当涉及到动态加载外部脚本时,这个功能非常有用,而您显然正在尝试这样做。
【讨论】:
做这种事情的清单:
func_name = function() {...} 的形式声明
更好的是,使用像Prototype 那样处理它的框架。你有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()"/>
现在在我们的本地 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
}
});
}
【讨论】:
这段代码也可以,而是评估 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);
}
【讨论】:
我常用的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() {...} 中看到的,所有<script> 元素都将从ajax 响应中收集并一一运行。它似乎对我很有效。最后两个参数是可选的。
【讨论】:
我已经对此进行了测试,并且可以正常工作。有什么问题?只需将新函数放入您的 javascript 元素中,然后调用它。它会起作用的。
【讨论】:
这听起来不是一个好主意。
您应该从 Ajax 方法返回的数据中抽象出要包含在其余 JavaScript 代码中的函数。
不过,尽管如此,(我不明白您为什么要在 div 中插入脚本块?)即使是在脚本块中编写的内联脚本方法也可以访问。
【讨论】:
我尝试了这里提供的所有技术,但最终可行的方法是将 JavaScript 函数放在应该发生的页面/文件中,并从 Ajax 的响应部分简单地作为函数调用它:
...
}, function(data) {
afterOrder();
}
这是第一次尝试,所以我决定分享。
【讨论】:
我今天通过将我的 JavaScript 放在响应 HTML 的底部解决了这个问题。
我有一个 AJAX 请求,它返回了一堆显示在叠加层中的 HTML。我需要将点击事件附加到返回的响应 HTML/覆盖中的按钮。在普通页面上,我会将我的 JavaScript 包装在“window.onload”或“$(document).ready”中,以便在呈现新叠加层的 DOM 后,它将事件处理程序附加到 DOM 对象,但是因为这是 AJAX 响应而不是新页面加载,所以该事件从未发生,浏览器从未执行我的 JavaScript,我的事件处理程序从未附加到 DOM 元素,我的新功能也不起作用。同样,我解决了我的“在 AJAX 响应中执行 JavaScript 问题”,方法是不在文档头部使用“$(document).ready”,而是将我的 JavaScript 放在文档末尾并让它在 HTML 之后运行/DOM 已被渲染。
【讨论】:
如果您的 AJAX 脚本运行时间超过几毫秒,则 eval() 将始终提前运行并在 AJAX 使用您尝试执行的脚本填充它之前评估空响应元素。
与其纠结于时间和 eval(),这里有一个非常简单的解决方法,应该适用于大多数情况,并且可能更安全一些。使用 eval() 通常不受欢迎,因为被评估为代码的字符很容易在客户端进行操作。
在这个例子中,我想在元素被添加到页面之后,将来自 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";
?>
【讨论】:
我需要做点什么,我发现这对我来说已经工作了很长时间,只是将其作为众多解决方案之一发布在这里,我喜欢没有 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;
}
【讨论】:
Federico Zancan's answer 是正确的,但您不必为脚本提供 ID 并评估所有脚本。只需评估您的函数名称即可调用它。
为了在我们的项目中实现这一点,我们编写了一个代理函数来调用 Ajax 响应中返回的函数。
function FunctionProxy(functionName){
var func = eval(functionName);
func();
}
【讨论】: