[javascript] innerHTML로 스크립트를 삽입 할 수 있습니까?

에서를 사용하여 일부 스크립트를 페이지에로드하려고 innerHTML했습니다 <div>. 스크립트가 DOM으로로드되는 것처럼 보이지만 (적어도 Firefox 및 Chrome에서는) 실행되지 않습니다. 스크립트를 삽입 할 때 스크립트를 실행하는 방법이 innerHTML있습니까?

샘플 코드 :

<!DOCTYPE html>
<html>
  <body onload="document.getElementById('loader').innerHTML = '<script>alert(\'hi\')<\/script>'">
    Shouldn't an alert saying 'hi' appear?
    <div id="loader"></div>
  </body>
</html>



답변

DOM 텍스트로 삽입 한 스크립트 코드를 실행 하려면 eval () 을 사용해야 합니다.

MooTools가 자동으로이를 수행하므로 jQuery도 버전에 따라 다릅니다. jQuery 버전 1.6 이상은을 사용 eval합니다. 이렇게하면 <script>태그 를 파싱하고 콘텐츠를 빠져 나오는 많은 번거 로움 과 다른 “gotchas”를 줄일 수 있습니다.

당신이거야 일반적으로 경우에 eval()그것은 자신을, 당신은 / 생성과 같은 모든 HTML 마크 업없이 스크립트 코드를 보내려면 <script>이되지 바와 같이, eval()제대로.


답변

문제에 대한 매우 흥미로운 해결책은 다음과 같습니다.
http://24ways.org/2005/have-your-dom-and-script-it-too

따라서 스크립트 태그 대신 이것을 사용하십시오.

<img src="empty.gif" onload="alert('test');this.parentNode.removeChild(this);" />


답변

다음은 모든 스크립트를 실행 가능한 스크립트로 재귀 적으로 대체하는 방법입니다.

function nodeScriptReplace(node) {
        if ( nodeScriptIs(node) === true ) {
                node.parentNode.replaceChild( nodeScriptClone(node) , node );
        }
        else {
                var i        = 0;
                var children = node.childNodes;
                while ( i < children.length ) {
                        nodeScriptReplace( children[i++] );
                }
        }

        return node;
}
function nodeScriptIs(node) {
        return node.tagName === 'SCRIPT';
}
function nodeScriptClone(node){
        var script  = document.createElement("script");
        script.text = node.innerHTML;
        for( var i = node.attributes.length-1; i >= 0; i-- ) {
                script.setAttribute( node.attributes[i].name, node.attributes[i].value );
        }
        return script;
}

호출 예 :

nodeScriptReplace(document.getElementsByTagName("body")[0]);


답변

스크립트를 작성한 다음 컨텐츠를 삽입 할 수 있습니다.

var g = document.createElement('script');
var s = document.getElementsByTagName('script')[0];
g.text = "alert(\"hi\");"
s.parentNode.insertBefore(g, s);

이것은 모든 브라우저에서 작동합니다 🙂


답변

이 코드를 사용했는데 정상적으로 작동합니다.

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


답변

innerHTML에서이 문제가 발생하여 Reactjs 애플리케이션의 “head”태그에 Hotjar 스크립트를 추가해야했으며 추가 직후 실행해야했습니다.

“헤드”태그로 동적 노드를 가져 오기위한 좋은 솔루션 중 하나는 React-helment 모듈입니다.


또한 제안 된 문제에 대한 유용한 솔루션이 있습니다.

innerHTML에 스크립트 태그가 없습니다!

HTML5에서는 innerHTML 속성을 사용하여 스크립트 태그를 동적으로 추가 할 수 없습니다. 따라서 다음이 실행되지 않으며 Hello World!라는 경고가 표시되지 않습니다.

element.innerHTML = "<script>alert('Hello World!')</script>";

이것은 HTML5 사양에 설명되어 있습니다.

참고 : innerHTML을 사용하여 삽입 된 스크립트 요소는 삽입 될 때 실행되지 않습니다.

그러나 이것이 innerHTML이 사이트 간 스크립팅으로부터 안전하다는 것을 의미하지는 않습니다. MDN의 innerHTML 페이지 에 설명 된대로 태그를 사용하지 않고 innerHTML을 통해 JavaScript를 실행할 수 있습니다 .

솔루션 : 동적으로 스크립트 추가

스크립트 태그를 동적으로 추가하려면 새 스크립트 요소를 작성하여 대상 요소에 추가해야합니다.

외부 스크립트에 대해이 작업을 수행 할 수 있습니다.

var newScript = document.createElement("script");
newScript.src = "http://www.example.com/my-script.js";
target.appendChild(newScript);

인라인 스크립트 :

var newScript = document.createElement("script");
var inlineScript = document.createTextNode("alert('Hello World!');");
newScript.appendChild(inlineScript);
target.appendChild(newScript);


답변

사람이 아직도이 일을하려고 들어, 아니, 당신은 사용하여 스크립트를 삽입 할 수 innerHTML있지만, 사용하여 스크립트 태그에 문자열을로드 할 수있다 BlobURL.createObjectURL.

문자열을 스크립트로 실행하고 약속을 통해 반환되는 스크립트의 ‘내보내기’를 얻을 수있는 예제를 만들었습니다.

function loadScript(scriptContent, moduleId) {
    // create the script tag
    var scriptElement = document.createElement('SCRIPT');

    // create a promise which will resolve to the script's 'exports'
    // (i.e., the value returned by the script)
    var promise = new Promise(function(resolve) {
        scriptElement.onload = function() {
            var exports = window["__loadScript_exports_" + moduleId];
            delete window["__loadScript_exports_" + moduleId];
            resolve(exports);
        }
    });

    // wrap the script contents to expose exports through a special property
    // the promise will access the exports this way
    var wrappedScriptContent =
        "(function() { window['__loadScript_exports_" + moduleId + "'] = " +
        scriptContent + "})()";

    // create a blob from the wrapped script content
    var scriptBlob = new Blob([wrappedScriptContent], {type: 'text/javascript'});

    // set the id attribute
    scriptElement.id = "__loadScript_module_" + moduleId;

    // set the src attribute to the blob's object url 
    // (this is the part that makes it work)
    scriptElement.src = URL.createObjectURL(scriptBlob);

    // append the script element
    document.body.appendChild(scriptElement);

    // return the promise, which will resolve to the script's exports
    return promise;
}

...

function doTheThing() {
    // no evals
    loadScript('5 + 5').then(function(exports) {
         // should log 10
        console.log(exports)
    });
}

실제 구현에서 이것을 단순화 했으므로 버그가 없다고 약속하지 않습니다. 그러나 원리는 효과가 있습니다.

스크립트가 실행 된 후 다시 값을 얻는 것에 신경 쓰지 않으면 훨씬 쉽습니다. Promiseand onload비트 는 그대로 두십시오 . 스크립트를 래핑하거나 전역 window.__load_script_exports_속성을 만들 필요조차 없습니다 .