并不是所有的内联JavaScript都是通过AJAX执行的

Not all inline JavaScript gets executed via AJAX

本文关键字:AJAX 执行 JavaScript 并不是      更新时间:2023-09-26

我正在通过AJAX加载文档的一个片段,我已经很好地加载了"外部"脚本,但我无法执行<script>标记中内联的所有JavaScript。

下面是我试图加载的文档片段/HTML的示例:

    <textarea></textarea>
    <script src="tinyMCE.js" class="ajax-script"></script>
    <script class="ajax-script">
        alert("I'm inline");
        tinymce.init({
            selector: 'textarea',
        });
    </script>

以下是我用来加载此文档(在XHR status 200上)的JavaScript代码:

    // * This response is HTML
    response = xhr.responseText;
    // * Set the innerHTML of the body to the response HTML
    document.body.innerHTML = response;
    // * Find all scripts with ajax-script class
    responseScripts = document.getElementsByClassName('ajax-script');
    // * Loop through all those scripts
    for (i = responseScripts.length; i--;) {
        // * Create a 'clone' script element
        cloneScript = document.createElement('script');
        // * If the response script has a src, add it to the clone
        if(responseScripts[0].src) {
            cloneScript.src = responseScripts[0].src;
        }
        // * If the response script has 'inline code', add it 
        if(responseScripts[0].innerHTML) {
            cloneScript.innerHTML = responseScripts[0].innerHTML;
        }
        // * Remove the original script
        responseScripts[0].parentNode.removeChild(responseScripts[0]);
        // * Append the clone script to the document
        document.body.appendChild(cloneScript);
    }

因此,在这个例子中,只有内联代码的alert("I'm inline");部分被执行,而没有其他部分。没有控制台错误,什么都没有,浏览器似乎忽略了tinymce.init()部分。

我不知道这是否与TinyMCE本身有关?但为什么会这样呢?我也尝试过对代码进行评估,但没有成功。文档加载后,我只需复制tinymce.init()并将其粘贴到控制台中,文本编辑器就会实际显示出来(因为tinymce.init()会被执行)。

有什么原因可以解释为什么只调用alert函数,而不调用其他函数?你认为这种加载脚本的方式有什么问题吗

谢谢。

尽管David Water的回应在Firefox中起到了作用,但在Chrome中却没有。因此,通过遵循jfriend的建议,我将动态列表变成了一个数组,并确保脚本同步加载。以下是我所做的:

response = xhr.responseText;
document.body.innerHTML = response;
responseScripts = document.getElementsByClassName('ajax-script');
i = 0;
// * Convert DOM dynamic list into an array
function listToArray(list) {
  var array = [];
  for (var i = list.length >>> 0; i--;) { 
    array[i] = list[i];
  }
  return array;
}
function loadScripts() {
    if(responseScripts[i]) {
        cloneScript = document.createElement('script');
        if(responseScripts[i].src) {
            cloneScript.src = responseScripts[i].src;
        }
        if(responseScripts[i].innerHTML) {
            cloneScript.innerHTML = responseScripts[i].innerHTML;
        }
        responseScripts[i].parentNode.removeChild(responseScripts[i]);
        document.body.appendChild(cloneScript);
        if(cloneScript.src) {
            // * For external scripts, wait 'till they load
            cloneScript.onload = function () {
                loadScripts(i++);
            };
        } else {
            // * For inline scripts, just call the function again
            loadScripts(i++);
        }
    }
}
if(responseScripts.length > 0) {
    responseScripts = listToArray(responseScripts);
    // * Start loading the scripts
    loadScripts();
}

以下是您的答案中的想法的清理版本(源自我的评论)。以下是改进的部分列表:

  1. 将其转换为一个函数,使所有代码的作用域都是本地的
  2. 声明了与var一起使用的所有变量
  3. 使用传入的参数使其通用,以便代码重用
  4. 避免了实际递归的堆栈堆积
  5. 将一些重复引用放入局部变量
  6. 使用Array.slice技巧复制数组
  7. 由于脚本标记只能有src=.innerHTML(不能同时有),因此将其设置为if/else
  8. 将一些常用代码合并到一个位置
  9. .innerHTML切换到.textContent,因为我们只需要文本

代码:

function insertHtmlAndExecutescripts(elem, html, cls) {
    elem.innerHTML = html;
    // make actual array of all ajax-script tags
    var scripts = Array.prototype.slice.call(elem.getElementsByClassName(cls), 0);
    var i = 0;
    function loadScripts() {
        if (i < scripts.length) {
            var cloneScript = document.createElement('script');
            var tag = scripts[i++];
            if (tag.src) {
                cloneScript.src = tag.src;
                // * For external scripts, wait 'till they load
                cloneScript.onload = function () {
                    loadScripts();
                }
                document.body.appendChild(cloneScript);
            } else if (tag.innerHTML) {
                cloneScript.textContent = tag.textContent;
                document.body.appendChild(cloneScript);
                // avoid stack build-up of actual recursion
                setTimeout(function() {
                    loadScripts();
                }, 0);
            }
            // remove the original embedded script tag (this is likely not necessary)
            tag.parentNode.removeChild(tag);
        }
    }
    loadScripts();
}

尝试记录您从responseScripts[0].innerHTML返回的内容,如果这是完整的脚本,那么您可以只记录eval的结果。如果这只是第一行,那么你已经发现了你的问题。