Executando <script> injetado por innerHTML após a chamada AJAX

100

Existe um div chamado "Conteúdo":

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

Deve ser preenchido com dados de um arquivo PHP, por AJAX, incluindo uma <script>tag. No entanto, o script dentro desta tag não está sendo executado.

<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
fonte
Como você está carregando o div? depende da biblioteca que você usa, normalmente você pode controlar se deseja apenas executar os scripts após o carregamento do ajax.
Bill Yang
1
Depois window.onloadcrio um XMTHttpRequestobjeto para solicitar outra página (php) que contém o conteúdo da div, incluindo um script. Estou fazendo isso com JS simples, sem bibliotecas (além da minha própria lol)
JCOC611

Respostas:

65

JavaScript inserido como texto DOM não será executado. No entanto, você pode usar o padrão de script dinâmico para atingir seu objetivo. A ideia básica é mover o script que você deseja executar para um arquivo externo e criar uma tag de script ao obter sua resposta Ajax. Em seguida, você define o srcatributo de sua tag de script e voila, carrega e executa o script externo.

Esta outra postagem StackOverflow também pode ser útil para você: Os scripts podem ser inseridos com innerHTML? .

Chocula
fonte
você pode selecionar todos os scripts carregados e executá-los pela função eval ():$('#audit-view script').each(function (index, element) { eval(element.innerHTML); })
Jerzy Gebler
Javascript adicionado à página deve ser executado. ( jsfiddle.net/wnn5fz3m/1 por exemplo). A questão é por que às vezes não?
NoBugs
@Chocula movi meu script para um arquivo separado ainda não funciona da parcial, você pode ajudar com isso? stackoverflow.com/questions/42941856/…
Samra
FYI (@NoBugs me dá uma pista). Eu tentei javascirpt element.innerHTML = zzz e desnt work. Então tentei Jquery $ (element) .html (zzz) e funcionou.
gtryonp
66

Usei este código, está funcionando bem

var arr = MyDiv.getElementsByTagName('script')
for (var n = 0; n < arr.length; n++)
    eval(arr[n].innerHTML)//run script inside div
Firas Nizam
fonte
4
Se você me perguntar, esta é uma resposta muito melhor do que a aceita. Isso é basicamente injeção de JavaScript.
Xedret
Embora útil, isso realmente não responde à pergunta de por que os scripts não rodam. Veja, por exemplo, jsfiddle.net/fkqmcaz7 e jsfiddle.net/wnn5fz3m/1 É absolutamente necessário executar, e não estou encontrando um caso simplificado de onde não funciona.
NoBugs
1
@NaoiseGolden OP está literalmente perguntando sobre a execução do script, então acho que está tudo bem aqui. :-)
dia
1
Eval () não é altamente recomendado. Veja isto - stackoverflow.com/questions/9107847/…
Reis,
15

Se você carregar um bloco de script em seu div via Ajax, como este:

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

... simplesmente atualiza o DOM da sua página, myFunction () não é necessariamente chamado.

Você pode usar um método de retorno de chamada Ajax, como o do jQuery método ajax () para definir o que executar quando a solicitação terminar.

O que você está fazendo é diferente de carregar uma página com JavaScript incluído desde o início (que é executado).

Um exemplo de como usar o callback de sucesso e o callback de erro depois de buscar algum conteúdo:

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

Outra maneira rápida e suja é usar eval () para executar qualquer código de script que você inseriu como texto DOM se não quiser usar jQuery ou outra biblioteca.

Christopher Tokar
fonte
usando o retorno de chamada do jQuery, como posso "executar" o código dentro do novo <script>?
JCOC611
Eu adicionei um exemplo de uso de retorno de chamada de sucesso:
Christopher Tokar
1
Muito obrigado! o "myFunction ();" parte era o que eu estava deixando de fora do meu código.
Jason,
11

Aqui está o script que avaliará todas as tags de script no texto.

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

Basta chamar esta função depois de receber seu HTML do servidor. Esteja avisado: o uso evalpode ser perigoso.

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

Evgenii
fonte
5

Isso 'simplesmente funciona' para mim usando jQuery, desde que você não tente anexar um subconjunto do HTML retornado por XHR ao documento. (Veja este relatório de bug mostrando o problema com o jQuery.)

Aqui está um exemplo que mostra como ele funciona:

<!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>

Aqui está o equivalente ao acima: http://jsfiddle.net/2CTLH/

Phrogz
fonte
1
Altamente improvável, essa seria a solução para o problema atualmente, já que esse era um bug com uma versão muito antiga do jQuery.
NoBugs
3

Aqui está uma função que você pode usar para analisar respostas AJAX, especialmente se você usar minifiedjs e quiser que ele execute o Javascript retornado ou apenas deseja analisar os scripts sem adicioná-los ao DOM, ela também lida com erros de exceção. Usei esse código na biblioteca php4sack e é útil fora da biblioteca.

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
fonte
2

Se você estiver injetando algo que precisa da tag de script, poderá obter um erro de sintaxe não detectado e dizer token ilegal . Para evitar isso, certifique-se de escapar das barras nas tags de fechamento do script. ie;

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

O mesmo vale para qualquer tag de fechamento, como uma tag de formulário.

TommyRay
fonte
0

Eu tinha uma postagem semelhante aqui, addEventListener load on ajax load SEM jquery

Como resolvi isso, insirai chamadas para funções dentro da minha função stateChange. A página que eu configurei tinha 3 botões que carregariam 3 páginas diferentes na contentArea. Como eu precisava saber qual botão estava sendo pressionado para carregar a página 1, 2 ou 3, poderia facilmente usar as instruções if / else para determinar qual página está sendo carregada e, em seguida, qual função executar. O que eu estava tentando fazer era registrar diferentes ouvintes de botão que só funcionariam quando a página específica fosse carregada por causa de IDs de elemento.

tão...

if (page1 está sendo carregado, pageload = 1) execute a função registerListeners1

então o mesmo para a página 2 ou 3.

Richard Chase
fonte
0

Isso funcionou para mim chamando eval em cada conteúdo de script de ajax .done:

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

Nota: Eu não escrevi parâmetros em $ .ajax que você deve ajustar de acordo com seu ajax.

Shivgre
fonte
0

Minha conclusão é que o HTML não permite tags SCRIPT NESTED. Se você estiver usando javascript para injetar código HTML que inclua tags de script dentro, não vai funcionar porque o javascript vai em uma tag de script também. Você pode testá-lo com o próximo código e verá que não vai funcionar. O caso de uso é que você está chamando um serviço com AJAX ou similar, está obtendo HTML e deseja injetá-lo no HTML DOM diretamente. Se o código HTML injetado tiver tags SCRIPT dentro, não vai funcionar.

<!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
fonte
-3

Outra coisa a fazer é carregar a página com um script como:

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

Isso carregará a página, executará o script e removerá o manipulador de eventos quando a função for executada. Isso não será executado imediatamente após um carregamento de ajax, mas se você estiver esperando que o usuário insira o elemento div, funcionará perfeitamente.

PS. Requer Jquery

Joshua Klein
fonte