Wykonywanie <script> wstrzykniętego przez innerHTML po wywołaniu AJAX

100

Istnieje element div o nazwie „Treść”:

<div id="content"></div>

Powinien być wypełniony danymi z pliku PHP, przez AJAX, w tym <script>tagiem. Jednak skrypt wewnątrz tego tagu nie jest wykonywany.

<div id="content"><!-- After AJAX loads the stuff that goes here -->
   <script type="text/javascript">
     //code
   </script>
   <!-- More stuff that DOES work here -->
</div>
JCOC611
źródło
Jak ładujesz div? zależy od używanej biblioteki, możesz normalnie kontrolować, czy chcesz po prostu wykonywać skrypty po załadowaniu Ajax.
Bill Yang,
1
Po window.onloadutworzeniu XMTHttpRequestobiektu w celu zażądania innej strony (php), która zawiera zawartość div, w tym skrypt. Robię to ze zwykłym JS, bez bibliotek (poza moim własnym
lolem

Odpowiedzi:

66

JavaScript wstawiony jako tekst DOM nie zostanie wykonany. Możesz jednak użyć dynamicznego wzorca skryptu, aby osiągnąć swój cel. Podstawowym pomysłem jest przeniesienie skryptu, który chcesz wykonać, do zewnętrznego pliku i utworzenie tagu skryptu, gdy otrzymasz odpowiedź Ajax. Następnie ustawsrc atrybut swojego tagu skryptu i voila, ładuje i wykonuje zewnętrzny skrypt.

Ten inny post StackOverflow może być również pomocny: Czy skrypty można wstawiać za pomocą innerHTML? .

Chocula
źródło
możesz wybrać wszystkie załadowane skrypty i uruchomić je funkcją eval ():$('#audit-view script').each(function (index, element) { eval(element.innerHTML); })
Jerzy Gebler
JavaScript dodany do strony bezwzględnie powinien być wykonywany. ( na przykład jsfiddle.net/wnn5fz3m/1 ). Pytanie brzmi, dlaczego czasami tak nie jest?
NoBugs
@Chocula Przeniosłem swój skrypt do osobnego pliku, ale nadal nie działa z częściowym, czy możesz w tym pomóc? stackoverflow.com/questions/42941856/…
Samra
FYI (@NoBugs daje mi wskazówkę). Próbowałem zwykłego javascirpt element.innerHTML = zzz i desnt work. Potem wypróbowałem Jquery $ (element) .html (zzz) i zadziałałem.
gtryonp
66

Użyłem tego kodu, działa dobrze

var arr = MyDiv.getElementsByTagName('script')
for (var n = 0; n < arr.length; n++)
    eval(arr[n].innerHTML)//run script inside div
Firas Nizam
źródło
4
Jeśli o mnie chodzi, jest to znacznie lepsza odpowiedź niż zaakceptowana. To jest prawie wstrzyknięcie JavaScript.
Xedret
Chociaż przydatne, to naprawdę nie odpowiada na pytanie, dlaczego skrypty nie działają. Zobacz na przykład jsfiddle.net/fkqmcaz7 i jsfiddle.net/wnn5fz3m/1 To absolutnie powinno działać i nie znajduję uproszczonego przypadku, w którym nie działa.
NoBugs
1
@NaoiseGolden OP dosłownie pyta o wykonanie skryptu, więc myślę, że tutaj jest w porządku. :-)
zmienia się
1
Eval () nie jest wysoce zalecane. Zobacz to - stackoverflow.com/questions/9107847/…
Kings
15

Jeśli ładujesz blok skryptu w swoim div przez Ajax w ten sposób:

<div id="content">
    <script type="text/javascript">
    function myFunction() {
      //do something
    }
    myFunction();
    </script>
</div>

... po prostu aktualizuje DOM twojej strony, myFunction () niekoniecznie jest wywoływana.

Możesz użyć metody wywołania zwrotnego Ajax, takiej jak ta w ajax () jQuery metodzie aby zdefiniować, co ma być wykonane po zakończeniu żądania.

To, co robisz, różni się od ładowania strony z zawartym w niej JavaScriptem od samego początku (co jest wykonywane).

Przykład użycia funkcji wywołania zwrotnego sukcesu i wywołania zwrotnego błędu po pobraniu zawartości:

  $.ajax({
    type: 'GET',
    url: 'response.php',
    timeout: 2000,
    success: function(data) {
      $("#content").html(data);
      myFunction();
    },
    error: function (XMLHttpRequest, textStatus, errorThrown) {
      alert("error retrieving content");
    }

Innym szybkim i nieuczciwym sposobem jest użycie eval () do wykonania dowolnego kodu skryptu, który wstawiłeś jako tekst DOM, jeśli nie chcesz używać jQuery lub innej biblioteki.

Christopher Tokar
źródło
używając wywołania zwrotnego jQuery, jak mogę „wykonać” kod w nowym <script>?
JCOC611,
Dodałem przykład wykorzystania wywołania zwrotnego sukcesu:
Christopher Tokar
1
Dziękuję bardzo! "myFunction ();" częścią było to, co pominąłem z mojego kodu.
Jason
11

Oto skrypt, który ocenia wszystkie znaczniki skryptu w tekście.

function evalJSFromHtml(html) {
  var newElement = document.createElement('div');
  newElement.innerHTML = html;

  var scripts = newElement.getElementsByTagName("script");
  for (var i = 0; i < scripts.length; ++i) {
    var script = scripts[i];
    eval(script.innerHTML);
  }
}

Po prostu wywołaj tę funkcję po otrzymaniu kodu HTML z serwera. Uwaga: używanie evalmoże być niebezpieczne.

Demo: http://plnkr.co/edit/LA7OPkRfAtgOhwcAnLrl?p=preview

Evgenii
źródło
5

To „po prostu działa” dla mnie przy użyciu jQuery, pod warunkiem, że nie spróbujesz dołączyć podzbioru kodu HTML zwróconego przez XHR do dokumentu. (Zobacz ten raport o błędzie przedstawiający problem z jQuery.)

Oto przykład pokazujący, że działa:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 
<html lang="en"> 
<head> 
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 
    <title>test_1.4</title> 
    <script type="text/javascript" charset="utf-8" src="jquery.1.4.2.js"></script> 
    <script type="text/javascript" charset="utf-8"> 
        var snippet = "<div><span id='a'>JS did not run<\/span><script type='text/javascript'>" +
        "$('#a').html('Hooray! JS ran!');" +
        "<\/script><\/div>";
        $(function(){
            $('#replaceable').replaceWith($(snippet));
        });
    </script> 
</head> 
<body> 
    <div id="replaceable">I'm going away.</div> 
</body> 
</html>

Oto odpowiednik powyższego: http://jsfiddle.net/2CTLH/

Phrogz
źródło
1
Jest to wysoce nieprawdopodobne, aby było to obecnie rozwiązanie problemu, ponieważ był to błąd w naprawdę starej wersji jQuery.
NoBugs
3

Oto funkcja, której możesz użyć do analizowania odpowiedzi AJAX, zwłaszcza jeśli używasz minifiedjs i chcesz, aby wykonywał zwrócony kod JavaScript lub po prostu chcesz przeanalizować skrypty bez dodawania ich do DOM, obsługuje również błędy wyjątków. Użyłem tego kodu w bibliotece php4sack i jest przydatny poza biblioteką.

function parseScript(_source) {
    var source = _source;
    var scripts = new Array();

    // Strip out tags
    while(source.toLowerCase().indexOf("<script") > -1 || source.toLowerCase().indexOf("</script") > -1) {
        var s = source.toLowerCase().indexOf("<script");
        var s_e = source.indexOf(">", s);
        var e = source.toLowerCase().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
         // window.alert('Script failed to run - '+scripts[i]);
          if (e instanceof SyntaxError) console.log (e.message+' - '+scripts[i]);
                    }
    }
// Return the cleaned source
    return source;
 }
Andre Van Zuydam
źródło
2

Jeśli wstrzykujesz coś, co wymaga tagu skryptu, możesz otrzymać nieprzechwycony błąd składni i powiedzieć nielegalny token . Aby tego uniknąć, pamiętaj o pominięciu ukośników w zamykających tagach skryptu. to znaczy;

var output += '<\/script>';

To samo dotyczy wszystkich tagów zamykających, takich jak tag formularza.

TommyRay
źródło
0

Miałem podobny post tutaj, ładowanie addEventListener na ładowanie ajax BEZ jquery

Jak rozwiązałem, wstawianie wywołań funkcji w ramach mojej funkcji stateChange. Strona, którą ustawiłem, zawierała 3 przyciski, które ładowały 3 różne strony do obszaru contentArea. Ponieważ musiałem wiedzieć, który przycisk został naciśnięty, aby załadować stronę 1, 2 lub 3, mogłem łatwo użyć instrukcji if / else, aby określić, która strona jest ładowana, a następnie którą funkcję uruchomić. Próbowałem zarejestrować różne odbiorniki przycisków, które działałyby tylko po załadowaniu określonej strony z powodu identyfikatorów elementów.

więc...

if (ładowana jest strona1, pageload = 1) uruchom funkcję registerListeners1

to samo dla strony 2 lub 3.

Richard Chase
źródło
0

To zadziałało dla mnie, wywołując eval dla każdej zawartości skryptu z ajax .done:

$.ajax({}).done(function (data) {      
    $('div#content script').each(function (index, element) { eval(element.innerHTML); 
})  

Uwaga: nie zapisałem parametrów do $ .ajax, które musisz dostosować zgodnie z twoim Ajax.

shivgre
źródło
0

Mój wniosek jest taki, że HTML nie zezwala na znaczniki ZAGROŻONY SKRYPT. Jeśli używasz javascript do wstrzykiwania kodu HTML, który zawiera tagi skryptu, nie zadziała, ponieważ javascript również znajduje się w tagu script. Możesz to przetestować następnym kodem i przekonasz się, że nie zadziała. Przykład użycia jest taki, że dzwonisz do usługi za pomocą AJAX lub podobnego, otrzymujesz HTML i chcesz bezpośrednio wstrzyknąć go do HTML DOM. Jeśli wstrzyknięty kod HTML ma wewnątrz znaczniki SCRIPT, nie zadziała.

<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"></head><body></body><script>document.getElementsByTagName("body")[0].innerHTML = "<script>console.log('hi there')</script>\n<div>hello world</div>\n"</script></html>
Antonio Martin
źródło
-3

Inną rzeczą do zrobienia jest załadowanie strony za pomocą skryptu takiego jak:

<div id="content" onmouseover='myFunction();$(this).prop( 'onmouseover', null );'>
<script type="text/javascript">
function myFunction() {
  //do something
}
myFunction();
</script>
</div>

Spowoduje to załadowanie strony, a następnie uruchomienie skryptu i usunięcie modułu obsługi zdarzeń po uruchomieniu funkcji. Nie uruchomi się to natychmiast po załadowaniu AJAX, ale jeśli czekasz, aż użytkownik wejdzie do elementu div, zadziała to dobrze.

PS. Wymaga Jquery

Joshua Klein
źródło