Динамически добавленный javascript с внешним script не выполняется

Итак, это наш сценарий... Прежде всего, мы добавляем фрагмент кода javascript, который добавляет внешний script для документирования следующим образом:

(function() {
     var e = document.createElement('script'); e.type = 'text/javascript'; e.async = true; e.src = 'http://blabla.com/script.js';
     var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(e, s);
})();

Затем в script.js происходит следующее:

function ajaxCall() { 
    //... some script to send ajax request which calls createDiv() after success
   if (window.XMLHttpRequest){
      xmlhttp=new XMLHttpRequest();
   }
   else{
      xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
   }
   xmlhttp.onreadystatechange=function(){
      if(xmlhttp.readyState==4&&xmlhttp.status==200){
         createDiv(xmlhttp.responseText);
      }
   };
   xmlhttp.open("GET","http://blabla.com/api/");
   xmlhttp.send(); 
}
function parseResponse(response) {
    var parser = new DOMParser();
    var dom = parser.parseFromString(response, "text/html");
    return dom;
}
function createDiv(responsetext)
{
   var dom = parseResponse(responsetext);

   var _ws = dom.getElementById('styles');
   var h = document.getElementsByTagName('head')[0];
   h.appendChild(document.importNode(_ws, true));

   var _jr = dom.getElementById('script1').outerHTML;
   var _vc = dom.getElementById('script2').outerHTML;
   var _rv = dom.getElementById('script3').outerHTML;

   var _rw = dom.getElementById('my_div');
   var _b = document.getElementsByTagName('body')[0];
   var _d = document.createElement('div'); _d.id = 'just_id';
   _d.innerHTML = _jr + _vc + _rv;
   _d.appendChild(document.importNode(_rw, true));
   _b.appendChild(_d);
}
ajaxCall();

Все работает отлично, script, а div добавляются как ожидалось, а ожидаемое, но добавленное script не выполняется и не влияет на добавленный div. Почему это? И как я могу заставить его выполнить? Мы также не получаем никаких предупреждений/ошибок в консоли. Использование последнего firefox.

РЕДАКТИРОВАТЬ 1

Комментарий: Пример script, который вы показали, просто определяет функцию, но на самом деле не называет их

Он фактически вызывает функцию, которая делает запрос ajax, пожалуйста, просмотрите отредактированный фрагмент кода.

РЕДАКТИРОВАТЬ 2

Комментарий: Вы пытались поместить логику в обратный вызов, а затем называть его в createDiv? Можно добавить несколько журналов, чтобы проверить, вызвано ли его вызовом, но он не может найти div

Я просто попробовал вызов console.log('hello world'); в одном из скриптов, добавляемых функцией createDiv(), но ничего не делает.

РЕДАКТИРОВАТЬ 3

Подробнее. Когда страница загружена, я вижу

<script type="text/javascript" id="script1" src="http://blabla.com/script1.js"></script>
<script type="text/javascript" id="script2" src="http://blabla.com/script2.js"></script>
<script type="text/javascript" id="script3" src="http://blabla.com/script3.js"></script>
<div id="just_id">Content here</div>

В DOM-Inspector и, как упоминалось выше, есть console.log('hello world');, чтобы проверить его, но он не выполняется.

EDIT 4

Добавлена ​​функция CSS через createDiv() для документа <head></head>, и она влияет на мой div с id 'just_id'. JS все еще не работает.

РЕДАКТИРОВАТЬ 5

Также попытался добавить встроенный javascript вместо внешнего в мою функцию, но все равно безуспешно. Может видеть следующее в DOM-Inspector, но код ничего не записывает.

<script type="text/javascript">
   console.log('test');
</script>

РЕДАКТИРОВАТЬ 6

Я также попытался импортировать узлы из предложенный здесь

Этот метод не позволяет перемещать узлы между различными документами. Если вы хотите добавить node из другого документа, необходимо использовать метод document.importNode().

а также попытался изменить порядок. 1.css 2.div 3.js, как это, но до сих пор нет успеха... У кого-нибудь есть идея, что здесь происходит?

Проверьте код выше

EDIT 7

Как предложено в Как выполнить динамически загруженный блок JavaScript? Я установил innerHTML моего div в скрипты + содержимое, а затем добавлен в DOM следующим образом:

Проверьте код выше

Но все равно без успеха.

EDIT 8

Добавлен код функции ajax. Третий параметр в xmlhttp.open("GET","http://blabla.com/api/"); true (асинхронный) и ложный (синхронный) уже пробовали. Все еще нет успеха.

РЕДАКТИРОВАТЬ 9

function createDiv(), как было предложено в ответ:

var dom = parseResponse(responsetext);

var _jr = dom.getElementById('script1'); //<script> tag with src attribute
var _vc = dom.getElementById('script2'); //inline <script> for test purposes only, it should actualy be external script also
var _rv = dom.getElementById('script3'); //inline <script>

var _rw = dom.getElementById('external_div');
var _b = document.getElementsByTagName('body')[0];
var _d = document.createElement('div'); _d.id = 'internal_div';
_d.appendChild(document.importNode(_rw, true));
_d.appendChild(document.importNode(_jr, true));
_d.appendChild(document.importNode(_rv, true)); //without second parameter TRUE, I dont get script content
_d.appendChild(document.importNode(_vc, true)); //without second parameter TRUE, I dont get script content
_b.appendChild(_d);

Не работает. Работайте, только если я принимаю innerHTML встроенных скриптов из другого документа, создайте такие элементы, как предложено в ответ, установите их innerHTML, а затем добавьте текущий документ. Внешний script вообще не будет загружаться.

EDIT 10

.htaccess from localhost...:

<Files *.html>
    Order Deny,Allow
    Deny from all
    Allow from 127.0.0.1
</Files>

<Files *.php>
    Order Deny,Allow
    Deny from all
    Allow from 127.0.0.1
</Files>

<Files index.php>
    Order Allow,Deny
    Allow from all
</Files>

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /page1/
#RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]
</IfModule>

.htaccess from 192. ***...:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /direktexpress/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /page2/index.php [L]
</IfModule>
# END WordPress

РЕДАКТИРОВАТЬ 11

Таким образом, я, вероятно, собираюсь дать баунти @Drakes, только для усилий и потраченного времени на мой вопрос, и покажу скриншоты о том, что происходит. Это, безусловно, ошибка firefox, я просто протестировал на хроме, и он работает.

1st picture: исходный код 192.168.2.197/testing/whatever/etc..htaccess остается таким же, как в примере выше

Исходный код для 192.168.2.197

2-е изображение - исходный код для script.js, который загружается на 192.168.2.197/testing/whatever/etc..htaccess остается таким же, как в примере выше:

Script.js Источник

Третье изображение - снимок экрана инспектора, как выглядит мой дом:

Dom

4-я фотография.... ничего не происходит. Просто предупреждение, которое не имеет никакого отношения к моей проблеме (надеюсь, так), потому что даже когда я удаляю все сценарии, он все равно появляется.

Консоль

Является ли это ошибкой firefox?

4b9b3361

Проблема в том, что приведенный выше код смешивает appendChild с innerHTML. Это не интуитивно очевидно, что в некоторых случаях они выполняются по-разному. Первый позволяет вводить вновь вставленные узлы script, поскольку они прикреплены к DOM. Последнее не делает. Для многих это был головорез. Просьба Google "script и innerHTML", и вы обнаружите, что не одиноки в этой проблеме.

Если вы (1) измените

_d.innerHTML = _jr + _vc + _rv

к

_d.appendChild(document.importNode(_jr));
_d.appendChild(document.importNode(_vc));
_d.appendChild(document.importNode(_rv));

и дополнительно (2) изменить

var _jr = dom.getElementById('script1').outerHTML;
var _vc = dom.getElementById('script2').outerHTML;
var _rv = dom.getElementById('script3').outerHTML;

к

var _jr = dom.getElementById('script1');
var _vc = dom.getElementById('script2');
var _rv = dom.getElementById('script3');

тогда ваши скрипты будут выполнены. Вот демонстрация:

var b = document.getElementsByTagName('body')[0];

/* Works */
var s1 = document.createElement('script');
s1.innerHTML = "console.log('I will execute')";
b.appendChild(s1);

/* Fails */
var s2 = document.createElement('script');
s2.innerHTML = "while(1){alert('Do not worry! I will not execute');}";
b.innerHTML = s2.outerHTML;
console.log("It didn't execute");

Испытано

Я запустил код OP на своем сервере, чтобы как можно лучше реплицировать проблему. Учитывая предоставленный код, я могу заставить его работать по своему усмотрению на моем сервере. Я только изменил URL-адрес AJAX от http://blabla.com/api/ до api.html и предоставил свой собственный файл, так как ни один из них не был предоставлен OP, но я собрал все, что он должен содержать из OP JavaScript). Вот примеры результатов:

index.html

<html lang="en">
<body>
    <script>
        (function() {
            var e = document.createElement('script'); e.type = 'text/javascript'; e.async = true; e.src = 'script.js';
            var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(e, s);
        })();
    </script>
</body>
</html>

script.js

Это код OP, измененный для моих спецификаций выше (используя appendChild вместо innerHTML, а вызов AJAX возвращает содержимое api.html ниже)

function ajaxCall() {
    //... some script to send ajax request which calls createDiv() after success
    if (window.XMLHttpRequest){
        xmlhttp=new XMLHttpRequest();
    }
    else{
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }
    xmlhttp.onreadystatechange=function(){
        if(xmlhttp.readyState==4&&xmlhttp.status==200){
            createDiv(xmlhttp.responseText);
        }
    };
    xmlhttp.open("GET","api.html");
    xmlhttp.send();
}
function parseResponse(response) {
    var parser = new DOMParser();
    var dom = parser.parseFromString(response, "text/html");
    return dom;
}
function createDiv(responsetext)
{
    var dom = parseResponse(responsetext);

    var _ws = dom.getElementById('styles');
    var h = document.getElementsByTagName('head')[0];
    h.appendChild(document.importNode(_ws, true));

    var _jr = dom.getElementById('script1');
    var _vc = dom.getElementById('script2');
    var _rv = dom.getElementById('script3');

    var _rw = dom.getElementById('my_div');
    var _b = document.getElementsByTagName('body')[0];
    var _d = document.createElement('div'); _d.id = 'just_id';

    _d.appendChild(document.importNode(_jr, true));
    _d.appendChild(document.importNode(_vc, true));
    _d.appendChild(document.importNode(_rv, true));

    _d.appendChild(document.importNode(_rw, true));
    _b.appendChild(_d);
}
ajaxCall();

api.html (возвращается вызовом AJAX)

<!DOCTYPE html>
<html>
<body>
    <style type="text/css" id="styles"></style>
    <script type="text/javascript" id="script1" src="script1.js"></script>
    <script type="text/javascript" id="script2">console.log("incline script 2")</script>
    <script type="text/javascript" id="script3">console.log("incline script 3")</script>
    <div id="my_div"></div>
</body>
</html>

script1.js

console.log("src script1");

Вот результаты DevTools, чтобы показать, что это работает.

Devtools inspection

Console results

8
22 июля '16 в 22:01
источник

JavaScript на странице уже проанализирован, вам нужно eval получить результат в контекст окна.

function evalRequest(url) {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            eval(xmlhttp.responseText);
        }
    }
    xmlhttp.open("GET", url, true);
    xmlhttp.send(null);
}; 

UPDATE: вам может понадобиться текст script как текст, а затем eval it)

function getScriptsAsText() {
    var div = document.createElement('div');
    var scripts = [];
    var scriptNodes = document.getElementsByTagName('script');

    for (var i = 0, iLen = scriptNodes.length; i < iLen; i++) {
        div.appendChild(scriptNodes[i].cloneNode(true));
        scripts.push(div.innerHTML);
        div.removeChild(div.firstChild);
    }
    return scripts;
};
4
25 июля '16 в 18:54
источник

В настоящее время вы загружаете script на свою страницу. Это происходит ПОСЛЕ того, как страница js уже была выполнена (поскольку это асинхронно и обычно загрузка внешнего ресурса занимает больше времени, чем выполнение script).

Итак... в основном у вас есть правильные определения функций и все, и вызывать их с консоли позже должны работать (я думаю, вы уже пробовали это и было хорошо).

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

Чтобы обойти это, вы можете создать запрос ajax на страницу с script и в своем обратном вызове вызвать любую функцию, которую вам нужно вызвать.

0
25 июля '16 в 13:34
источник
    (function () {
        var s = document.getElementsByTagName('script')[0];
        s.parentNode.insertBefore(CreateScriptTag('http://blabla.com/script.js',true), s);
    })();
    function CreateScriptTag(src, isAsync) {
        var e = document.createElement('script')
        e.type = 'text/javascript';
        e.async = isAsync;
        e.src = src;
    return e;
    }
    function ajaxCall() {
        //... some script to send ajax request which calls createDiv() after success
        if (window.XMLHttpRequest) xmlhttp = new XMLHttpRequest();
        else  xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        xmlhttp.open("GET", "http://blabla.com/api/");
        xmlhttp.onreadystatechange = function () { if (xmlhttp.readyState === 4 && xmlhttp.status === 200) createDiv(xmlhttp.responseText); };
        xmlhttp.send();
    }
    function parseResponse(response) {
        var parser = new DOMParser();
        var dom = parser.parseFromString(response, "text/html");
        return dom;
    }
    function createDiv(responsetext) {
        var head=document.getElementsByTagName('head')[0],dom = parseResponse(responsetext),_div=document.createElement('div');
        head.appendChild(document.importNode(dom.getElementById('styles'), true)); //Assuming stylesheets are working fine.
        _div.id = 'just_id';
        //div.innerHTML = _script1 + _script2 + _script3;
        document.getElementsByTagName('body')[0].appendChild(_div.appendChild(dom.getElementById('my_div')));

        head.appendChild(CreateScriptTag(dom.getElementById('script1').getAttribute("src"),true));
        head.appendChild(CreateScriptTag(dom.getElementById('script2').getAttribute("src"),true));
        head.appendChild(CreateScriptTag(dom.getElementById('script3').getAttribute("src"),true));

    /* or another idea;
        document.write(_script1 + _script2 + _script3);
    */
    }
    ajaxCall();
0
28 июля '16 в 1:00
источник

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

  • Самый простой способ - использовать jQuery $(document).append вместо document.createElement, если вы не возражаете против использования библиотеки.

  • Вы также можете использовать jQuery $.getScript.

  • Если вы предпочитаете не включать 3-ю библиотеку, вам нужно подумать о способе задержки загрузки страницы, когда вы вставляете свои скрипты, что-то вроде alert, чтобы создать окно подсказки, но пользовательский интерфейс не будет так хорошо, подумайте о более плавном пути.

  • Используйте XMLHTTPRequest для извлечения ваших скриптов. Это синхронно, и я вижу, что вы знакомы с ним, потому что вы уже используете его в своем script.

Удачи и счастливого кодирования.

0
28 июля '16 в 17:00
источник

Вы пытались получить URL-адрес файла src и вызывать его с помощью функции jQuery getScript()?

Измените функцию creatediv():

var _jr = dom.getElementById('script1').outerHTML;

var srcScript1Left = _jr.split('src="');
srcScript1Right = srcScript1Left[1].split('"');
srcScript1 = srcScript1Right[0];

console.log(srcScript1);

$.getScript(srcScript1, function()
{
    // The external script has been executed.
});

ИЗМЕНИТЬ

Аналогичный метод в чистом Javascript:

var _jr = dom.getElementById('script1').outerHTML;

var srcScript1Left = _jr.split('src="');
srcScript1Right = srcScript1Left[1].split('"');
srcScript1 = srcScript1Right[0];

console.log(srcScript1); // Display the name of the external script file

if(window.XMLHttpRequest) {
    xmlhttp=new XMLHttpRequest();
} else {
    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function() {
    if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {
        eval(xmlhttp.responseText); // Execute the external script
    }
};
xmlhttp.open("GET",srcScript1);
xmlhttp.send();
0
29 июля '16 в 1:47
источник