Выполнение <script>, введенного innerHTML после вызова AJAX


100

Есть div под названием Content:

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

Он должен быть заполнен данными из файла PHP с помощью AJAX, включая <script>тег. Однако сценарий внутри этого тега не выполняется.

<div id="content"><!-- After AJAX loads the stuff that goes here -->
   <script type="text/javascript">
     //code
   </script>
   <!-- More stuff that DOES work here -->
</div>

Как вы загружаете div? В зависимости от библиотеки, которую вы используете, вы обычно можете контролировать, хотите ли вы просто выполнять сценарии после загрузки ajax.
Билл Ян

1
После того, как window.onloadя создам XMTHttpRequestобъект для запроса другой (php) страницы, содержащей содержимое div, включая скрипт. Я делаю это с помощью простого JS, без библиотек (кроме моего собственного лол)
JCOC611 06

Ответы:


66

JavaScript, вставленный как текст DOM, не будет выполняться. Однако вы можете использовать динамический шаблон сценария для достижения своей цели. Основная идея состоит в том, чтобы переместить сценарий, который вы хотите выполнить, во внешний файл и создать тег сценария, когда вы получите ответ Ajax. Затем вы устанавливаете srcатрибут своего тега скрипта и вуаля, он загружает и выполняет внешний скрипт.

Другой пост на StackOverflow также может быть вам полезен: можно ли вставлять скрипты с помощью innerHTML? .


вы можете выбрать все загруженные скрипты и выполнить их с помощью функции eval ():$('#audit-view script').each(function (index, element) { eval(element.innerHTML); })
Ежи Геблер,

Javascript, добавленный на страницу, обязательно должен выполняться. ( например, jsfiddle.net/wnn5fz3m/1 ). Вопрос в том, почему иногда этого не происходит?
NoBugs

@Chocula, я переместил свой скрипт в отдельный файл, но он не работает частично, вы можете помочь с этим? stackoverflow.com/questions/42941856/…
Самра

К вашему сведению (@NoBugs подсказывает мне). Я попробовал простой элемент javascirpt element.innerHTML = zzz и desnt. Затем я пробую Jquery $ (element) .html (zzz) и работал.
gtryonp

66

Я использовал этот код, он работает нормально

var arr = MyDiv.getElementsByTagName('script')
for (var n = 0; n < arr.length; n++)
    eval(arr[n].innerHTML)//run script inside div

4
Если вы спросите меня, это гораздо лучший ответ, чем принятый. Это в значительной степени JavaScript-инъекция.
Xedret

Хотя это полезно, на самом деле это не отвечает на вопрос, почему скрипты не запускаются. См., Например, jsfiddle.net/fkqmcaz7 и jsfiddle.net/wnn5fz3m/1 Он обязательно должен работать, и я не нахожу упрощенного случая, когда он не работает.
NoBugs

1
@NaoiseGolden OP буквально спрашивает о выполнении скрипта, так что я думаю, что здесь все в порядке. :-)
thedayturns 01

1
Eval () не рекомендуется. См. Это - stackoverflow.com/questions/9107847/…
Kings

15

Если вы загрузите блок скрипта в свой div через Ajax следующим образом:

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

... он просто обновляет DOM вашей страницы, myFunction () не обязательно вызывается.

Вы можете использовать метод обратного вызова Ajax, такой как метод jQuery ajax (), чтобы определить, что выполнять после завершения запроса.

То, что вы делаете, отличается от загрузки страницы с включенным в нее JavaScript с самого начала (который действительно выполняется).

Пример того, как использовать обратный вызов успеха и обратный вызов ошибки после получения некоторого контента:

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

Другой быстрый и грязный способ - использовать eval () для выполнения любого кода сценария, который вы вставили как текст DOM, если вы не хотите использовать jQuery или другую библиотеку.


используя обратный вызов jQuery, как я могу «выполнить» код внутри нового <script>?
JCOC611 06

Я добавил пример использования обратного вызова успеха:
Кристофер Токар

1
Спасибо вам большое! "myFunction ();" часть была тем, что я упускал из своего кода.
Джейсон

11

Вот сценарий, который оценивает все теги сценария в тексте.

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

Просто вызовите эту функцию после получения HTML-кода с сервера. Будьте осторожны: использование evalможет быть опасным.

Демо: http://plnkr.co/edit/LA7OPkRfAtgOhwcAnLrl?p=preview


5

У меня это «просто работает» с использованием jQuery, при условии, что вы не пытаетесь добавить в документ подмножество HTML-кода, возвращенного XHR. (См. Этот отчет об ошибке показана проблема с jQuery.)

Вот пример, показывающий, как это работает:

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

Вот эквивалент приведенного выше: http://jsfiddle.net/2CTLH/


1
Маловероятно, что это было бы решением проблемы в настоящее время, поскольку это была ошибка действительно старой версии jQuery.
NoBugs

3

Вот функция, которую вы можете использовать для анализа ответов AJAX, особенно если вы используете minifiedjs и хотите, чтобы он выполнял возвращенный Javascript или просто хотите анализировать сценарии, не добавляя их в DOM, он также обрабатывает ошибки исключения. Я использовал этот код в библиотеке php4sack, и он полезен вне библиотеки.

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

2

Если вы вводите что-то, для чего нужен тег скрипта, вы можете получить неперехваченную синтаксическую ошибку и сказать незаконный токен . Чтобы этого избежать, избегайте косых черт в закрывающих тегах скрипта. т.е.

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

То же самое касается любых закрывающих тегов, таких как тег формы.


0

У меня здесь был похожий пост, загрузка addEventListener при загрузке ajax БЕЗ jquery

Как я решил, это было вставить вызовы функций в мою функцию stateChange. На странице, которую я настроил, было 3 кнопки, которые загружали 3 разные страницы в contentArea. Поскольку мне нужно было знать, какая кнопка была нажата для загрузки страницы 1, 2 или 3, я мог легко использовать операторы if / else, чтобы определить, какая страница загружается, а затем какую функцию запускать. Я пытался зарегистрировать разные прослушиватели кнопок, которые работали бы только при загрузке определенной страницы из-за идентификаторов элементов ..

так...

если (page1 загружается, pageload = 1) запустить функцию registerListeners1

то же самое для страницы 2 или 3.


0

Это сработало для меня, вызвав eval для каждого содержимого скрипта из ajax .done:

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

Примечание: я не записывал параметры в $ .ajax, которые вам нужно настроить в соответствии с вашим ajax.


0

Мой вывод: HTML не поддерживает теги NESTED SCRIPT. Если вы используете javascript для внедрения HTML-кода, который включает теги сценария внутри, это не сработает, потому что javascript также входит в тег сценария. Вы можете проверить это с помощью следующего кода, и вы убедитесь, что он не будет работать. Вариант использования: вы вызываете службу с помощью AJAX или аналогичного, вы получаете HTML и хотите напрямую вставить его в HTML DOM. Если введенный HTML-код содержит теги SCRIPT, работать не будет.

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

-3

Еще нужно загрузить страницу с помощью скрипта, например:

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

Это загрузит страницу, затем запустит сценарий и удалит обработчик событий, когда функция будет запущена. Это не будет запускаться сразу после загрузки ajax, но если вы ждете, пока пользователь введет элемент div, это будет работать нормально.

PS. Требуется JQuery

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.