web-dev-qa-db-ja.com

Ajax応答から返されたJavaScript関数を呼び出す

関数を含むスクリプトブロックを返すAjaxコマンドを送信するシステムがあります。このデータがDIVに正しく挿入された後、この関数を呼び出して必要なアクションを実行できるようにします。

これは可能ですか?

68
williamtroup

このフォームの下であなたの質問を正しく解釈すると思います: "OK、私はすでにすべてのAjaxのことで終わっています。DIVに挿入されたJavaScript関数がその瞬間からいつでも呼び出し可能かどうかを知りたいだけです。 、つまり、コールバックreturnのコンテキストで呼び出したくない」。

OK、このようなことを意味する場合、答えは「はい」です。次の条件下で、ブラウザー内のページの永続化中、いつでもその時点で新しいコードを呼び出すことができます。

1)Ajaxコールバックによって返されるJavaScriptコードは、構文的に正常でなければなりません。
2)関数宣言が既存の_<script>_要素内の_<div>_ブロックに挿入されたとしても、宣言コードは一度も存在しないため、ブラウザーは新しい関数が存在することを知りません実行されました。したがって、新しい関数を効果的に宣言し、ページ全体の有効期間中に使用可能にするには、Ajaxコールバックによって返された宣言コードをeval()する必要があります。

かなりダミーであっても、このコードはアイデアを説明します。

_<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の呼び出しを繰り返す必要があります。つまり、同じ関数のコンテキストが変更される場合、または宣言された関数の永続性が懸念される場合は、このスレッドの推奨例の1つに設計を変更することを真剣に検討する必要があります.

最後に、私があなたの質問を誤解し、あなたがあなたのAjaxコールバックが戻ったときに関数の呼び出しcontextualについて話しているなら、私の気持ちはプロトタイプアプローチ krosenvoldで説明 。クロスブラウザであり、テスト済みで完全に機能するため、将来の実装のためのより良いロードマップを提供できます。

73
Federico Zancan

:eval()は簡単に悪用される可能性があります。リクエストは第三者によって傍受され、信頼されていないコードを送信するとします。次に、eval()を使用して、この信頼されていないコードを実行します。 eval()の危険性 についてはこちらを参照してください。


返されたHTML/Ajax/JavaScriptファイル内には、JavaScriptタグがあります。 runscriptのようなIDを与えます。これらのタグに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で動作します。

42
Dandymon

それは完全に可能であり、これにはかなり正当なユースケースさえあります。 プロトタイプ フレームワークを使用すると、次のようになります。

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

Ajaxアップデーターの documentation を参照してください。オプションは 共通オプションセット にあります。いつものように、「this」が指す場所についていくつかの注意事項があるので、細かい活字を読んでください。

JavaScriptコードはロード時に評価されます。コンテンツに関数myFunc(),が含まれている場合、後で実際にmyFunc()と言うことができます。たぶん次のように。

_if (window["myFunc"])
   myFunc()
_

これにより、関数が存在するかどうかが確認されます。たぶん誰かがインターネットdoingエクスプローラ6で動作するより良いクロスブラウザの方法を持っています。

9
krosenvold

これはコードのかなり奇妙なデザインのようです。通常、関数を.jsファイルから直接呼び出して、Ajax呼び出しでのみデータを取得する方が理にかなっています。

ただし、応答で eval() を呼び出すことで機能するはずです-構文的に正しいJavaScriptコードであれば。

6
DanSingerman

JQueryでは、 getScript を使用してそれを行います

5
kgiannakakis

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
Daniel Hartmann

JQueryには、コードをグローバルに評価できるeval関数があり、コンテキストの問題を取り除くことができます。この関数は globalEval() と呼ばれ、私の目的にはうまく機能しました。そのドキュメントは here にあります。

これは、jQuery APIドキュメントで提供されるサンプルコードです。

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

test();
// newVar === true

この関数は、明らかにしようとしていた外部スクリプトを動的にロードする場合に非常に便利です。

3
beta

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
                  }


        });
}
2
Sultanos

そのようなことをするためのチェックリスト:

  1. 返されるAjax応答はeval(ed)です。
  2. 関数はfunc_name = function() {...}の形式で宣言されます

さらに良いことに、 Prototype のようにそれを処理するフレームワークを使用します。あなたが持っている Ajax.updater

2
Nasaralla

このコードも同様に機能しますが、代わりに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
user2054758

私の通常の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は、ターゲット要素の一時テキストになります。
finishCBは、ajaxトランザクションが正常に終了したときに呼び出されます。
xhr.onreadystatechange = function() {...}にあるように、_<script>_要素はすべて、ajax応答から収集され、1つずつ実行されます。それは私にとって非常にうまくいくようです。最後の2つのパラメーターはオプションです。

1
Ray

これは良い考えのようには聞こえません。

Ajaxメソッドによって返されたデータからJavaScriptコードの残りに含める関数を抽象化する必要があります。

しかし、それが価値があることについては(そして、スクリプトブロックをdivに挿入する理由がわかりませんか?)、スクリプトブロックで記述されたインラインスクリプトメソッドでもアクセスできます。

1
annakata

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

私はこれをテストしましたが、動作します。どうしたの?新しい関数をjavascript要素内に配置してから呼び出します。それが動作します。

0
user875234

Federico Zancanの答え は正しいですが、スクリプトにIDを与えてすべてのスクリプトを評価する必要はありません。関数名を評価するだけで、呼び出すことができます。

プロジェクトでこれを実現するために、Ajax応答内で返される関数を呼び出すプロキシ関数を作成しました。

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

ここで提供されているすべてのテクニックを試しましたが、最終的に機能する方法は、JavaScript関数をページ/ファイル内に配置して、Ajaxの応答部分から単純に関数として呼び出すことです。

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

これは最初の試みで機能したので、共有することにしました。

0
Sagive SEO

JavaScriptを応答HTMLの下部に配置することで、今日これを解決しました。

AJAXリクエストがあり、オーバーレイに表示されるHTMLの束を返しました。返された応答HTML /オーバーレイのボタンにクリックイベントを添付する必要がありました。通常のページでは、 JavaScriptを「window.onload」または「$(document).ready」でラップして、新しいオーバーレイのDOMがレンダリングされた後にイベントハンドラーをDOMオブジェクトにアタッチしますが、これは= AJAX応答であり、新しいページの読み込みではありません。そのイベントは発生しません。ブラウザはJavaScriptを実行しません。イベントハンドラーはDOM要素にアタッチされず、新しい機能は動作しませんでした繰り返しになりますが、「$(document).ready」をドキュメントの先頭に使用せず、JavaScriptを最後に配置することで、「AJAX応答問題でJavaScriptを実行する」を解決しました。ドキュメントのHTMLおよびDOMがレンダリングされた後に実行すること。

0
Andrew Koper