Nice programing

Ajax 응답에서 반환 된 JavaScript 함수 호출

nicepro 2020. 11. 7. 10:30
반응형

Ajax 응답에서 반환 된 JavaScript 함수 호출


함수가 포함 된 스크립트 블록을 반환하는 Ajax 명령을 보내는 시스템이 있습니다. 이 데이터가 DIV에 올바르게 삽입 된 후이 함수를 호출하여 필요한 작업을 수행 할 수 있기를 원합니다.

이것이 가능한가?


나는 다음과 같은 형식으로 귀하의 질문을 올바르게 해석한다고 생각합니다. "OK, 이미 모든 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 접근 방식을 제안하는 것입니다. 이는 향후 구현을위한 더 나은 로드맵을 제공 할 수 있습니다.


참고 : eval ()은 쉽게 오용 될 수 있습니다. 요청이 제 3 자에 의해 가로 채서 신뢰할 수없는 코드를 전송한다고 가정 해 보겠습니다. 그런 다음 eval ()을 사용하면 신뢰할 수없는 코드를 실행할 수 있습니다. eval ()위험성에 대해서는 여기를 참조하십시오 .


반환 된 HTML / Ajax / JavaScript 파일 안에 JavaScript 태그가 있습니다. runscript 와 같은 ID를 지정하십시오 . 이러한 태그에 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에서 작동합니다.


완전히 가능하며 이에 대한 합법적 인 사용 사례도 있습니다. Prototype 프레임 워크를 사용하여 다음과 같이 수행됩니다.

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

Ajax 업데이터 문서참조하십시오 . 옵션은 공통 옵션 세트에 있습니다. 평소와 같이 "this"가 가리키는 위치에 대한 몇 가지주의 사항이 있으므로 작은 글씨를 읽으십시오.

JavaScript 코드는로드시 평가됩니다. 콘텐츠에 기능 myFunc(),포함되어 있으면 myFunc()나중에 말할 수 있습니다 . 아마도 다음과 같습니다.

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

함수가 존재하는지 확인합니다. 누군가 Internet Explorer 6에서 작동하는 더 나은 크로스 브라우저 방식을 가지고있을 수 있습니다.


코드에 대한 다소 이상한 디자인 인 것 같습니다. 일반적으로 함수를 .js 파일에서 직접 호출 한 다음 Ajax 호출로만 데이터를 검색하는 것이 더 합리적입니다.

그러나 구문 적으로 올바른 JavaScript 코드 인 경우 응답에서 eval ()호출하여 작동해야한다고 생각합니다 .


jQuery를 사용하면 getScript를 사용하여 수행합니다.


아약스를 통해 아래의 방법으로 함수를 생성했다면 기억하세요 ...

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 () 이라고 하며 내 목적에 맞게 잘 작동했습니다. 문서는 여기 에서 찾을 수 있습니다 .

다음은 jQuery API 문서에서 제공하는 예제 코드입니다.

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

test();
// newVar === true

이 기능은 분명히하려고했던 외부 스크립트를 동적으로로드 할 때 매우 유용합니다.


이러한 작업을 수행하기위한 체크리스트 :

  1. 반환 된 Ajax 응답은 eval (ed)입니다.
  2. 함수는 형식으로 선언됩니다. func_name = function() {...}

더 좋은 방법은 Prototype 에서처럼 처리하는 프레임 워크를 사용하는 것입니다 . 당신은 Ajax.updater.


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
                  }


        });
}

이것은 좋은 생각처럼 들리지 않습니다.

Ajax 메소드에서 반환 된 데이터에서 나머지 JavaScript 코드에 포함 할 함수를 추상화해야합니다.

그러나 그만한 가치가있는 것은 (그리고 div에 스크립트 블록을 삽입하는 이유를 이해하지 못합니까?) 스크립트 블록에 작성된 인라인 스크립트 메서드도 액세스 할 수 있습니다.


이 코드도 작동합니다. 대신 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);
}

내 평소 아약스 호출 기능 :

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

몇 가지 설명 :
targetIdajax 호출 결과 텍스트가 들어갈 (일반적으로 div) 요소 ID입니다.
urlajax 호출 URL입니다.
busyMsg대상 요소의 임시 텍스트가됩니다.
finishCBajax 트랜잭션이 성공적으로 완료되면 호출됩니다.
보시다시피 xhr.onreadystatechange = function() {...}모든 <script>요소는 ajax 응답에서 수집되고 하나씩 실행됩니다. 저에게 아주 잘 작동하는 것 같습니다. 마지막 두 매개 변수는 선택 사항입니다.


나는 이것을 테스트했고 작동합니다. 뭐가 문제 야? 자바 스크립트 요소 안에 새 함수를 넣은 다음 호출하면됩니다. 작동합니다.


여기에 제공된 모든 기술을 시도했지만 마지막으로 작동하는 방법은 JavaScript 함수를 발생해야하는 페이지 / 파일 내부에 넣고 Ajax의 응답 부분에서 단순히 함수로 호출하는 것입니다.

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

이것은 첫 번째 시도에서 작동했기 때문에 공유하기로 결정했습니다.


오늘은 응답 HTML의 맨 아래에 JavaScript를 배치하여이 문제를 해결했습니다.

I had an AJAX request that returned a bunch of HTML that was displayed in an overlay. I needed to attach a click event to a button in the returned response HTML/overlay. On a normal page, I would wrap my JavaScript in a "window.onload" or "$(document).ready" so that it would attach the event handler to the DOM object after the DOM for the new overlay had been rendered, but because this was an AJAX response and not a new page load, that event never happened, the browser never executed my JavaScript, my event handler never got attached to the DOM element, and my new piece of functionality didn't work. Again, I solved my "executing JavaScript in an AJAX response problem" by not using "$(document).ready" in the head of the document, but by placing my JavaScript at the end of the document and having it run after the HTML/DOM had been rendered.


If your AJAX script takes more than a couple milliseconds to run, eval() will always run ahead and evaluate the empty response element before AJAX populates it with the script you're trying to execute.

Rather than mucking around with timing and eval(), here is a pretty simple workaround that should work in most situations and is probably a bit more secure. Using eval() is generally frowned upon because the characters being evaluated as code can easily be manipulated client-side.

Concept

  1. Include your javascript function in the main page. Write it so that any dynamic elements can be accepted as arguments.
  2. In your AJAX file, call the function by using an official DOM event (onclick, onfocus, onblur, onload, etc.) Depending on what other elements are in your response, you can get pretty clever about making it feel seamless. Pass your dynamic elements in as arguments.
  3. When your response element gets populated and the event takes place, the function runs.

Example

In this example, I want to attach a dynamic autocomplete list from the jquery-ui library to an AJAX element AFTER the element has been added to the page. Easy, right?

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

Federico Zancan's answer is correct but you don't have to give your script an ID and eval all your script. Just eval your function name and it can be called.

To achieve this in our project, we wrote a proxy function to call the function returned inside the Ajax response.

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

참고URL : https://stackoverflow.com/questions/510779/calling-a-javascript-function-returned-from-an-ajax-response

반응형