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
있지만, 사용하여 스크립트 태그에 문자열을로드 할 수있다 Blob
및 URL.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)
});
}
실제 구현에서 이것을 단순화 했으므로 버그가 없다고 약속하지 않습니다. 그러나 원리는 효과가 있습니다.
스크립트가 실행 된 후에 다시 값을 얻는 것에 신경 쓰지 않으면 훨씬 쉽습니다. Promise
and onload
비트 는 그대로 두십시오 . 스크립트를 래핑하거나 전역 window.__load_script_exports_
속성을 만들 필요조차 없습니다 .
광고 서버에서 사용하는 요소의 innerHTML을 설정하는 재귀 함수는 다음과 같습니다.
// o: container to set the innerHTML
// html: html text to set.
// clear: if true, the container is cleared first (children removed)
function setHTML(o, html, clear) {
if (clear) o.innerHTML = "";
// Generate a parseable object with the html:
var dv = document.createElement("div");
dv.innerHTML = html;
// Handle edge case where innerHTML contains no tags, just text:
if (dv.children.length===0){ o.innerHTML = html; return; }
for (var i = 0; i < dv.children.length; i++) {
var c = dv.children[i];
// n: new node with the same type as c
var n = document.createElement(c.nodeName);
// copy all attributes from c to n
for (var j = 0; j < c.attributes.length; j++)
n.setAttribute(c.attributes[j].nodeName, c.attributes[j].nodeValue);
// If current node is a leaf, just copy the appropriate property (text or innerHTML)
if (c.children.length == 0)
{
switch (c.nodeName)
{
case "SCRIPT":
if (c.text) n.text = c.text;
break;
default:
if (c.innerHTML) n.innerHTML = c.innerHTML;
break;
}
}
// If current node has sub nodes, call itself recursively:
else setHTML(n, c.innerHTML, false);
o.appendChild(n);
}
}
여기서 데모를 볼 수 있습니다 .
Krasimir Tsonev는 모든 문제를 극복 할 수있는 훌륭한 솔루션을 제공합니다. 그의 방법은 eval을 사용할 필요가 없으므로 성능이나 보안 문제가 없습니다. 내부 HTML 문자열에 js가 포함 된 html을 포함하도록 설정하고 즉시 DOM 요소로 변환하는 동시에 코드와 함께 존재하는 js 부분을 실행할 수 있습니다. 짧고 간단하며 원하는대로 정확하게 작동합니다.
그의 해결책을 즐기십시오.
http://krasimirtsonev.com/blog/article/Convert-HTML-string-to-DOM-element
중요 사항 :
- 대상 요소를 div 태그로 감싸 야합니다.
- src 문자열을 div 태그로 감싸 야합니다.
- src 문자열을 직접 작성하고 여기에 js 부분이 포함 된 경우 문자열이므로 닫는 스크립트 태그 (\ before /)를 올바르게 작성하십시오.
$(parent).html(code)
대신에 사용하십시오 parent.innerHTML = code
.
다음은 속성을 document.write
통해로드 된 스크립트와 사용하는 스크립트도 수정 src
합니다. 불행히도이 기능은 Google 애드 센스 스크립트에서 작동하지 않습니다.
var oldDocumentWrite = document.write;
var oldDocumentWriteln = document.writeln;
try {
document.write = function(code) {
$(parent).append(code);
}
document.writeln = function(code) {
document.write(code + "<br/>");
}
$(parent).html(html);
} finally {
$(window).load(function() {
document.write = oldDocumentWrite
document.writeln = oldDocumentWriteln
})
}
template 및 document.importNode를 사용해보십시오. 예를 들면 다음과 같습니다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Sample</title>
</head>
<body>
<h1 id="hello_world">Sample</h1>
<script type="text/javascript">
var div = document.createElement("div");
var t = document.createElement('template');
t.innerHTML = "Check Console tab for javascript output: Hello world!!!<br/><script type='text/javascript' >console.log('Hello world!!!');<\/script>";
for (var i=0; i < t.content.childNodes.length; i++){
var node = document.importNode(t.content.childNodes[i], true);
div.appendChild(node);
}
document.body.appendChild(div);
</script>
</body>
</html>
다음과 같이 할 수 있습니다.
var mydiv = document.getElementById("mydiv");
var content = "<script>alert(\"hi\");<\/script>";
mydiv.innerHTML = content;
var scripts = mydiv.getElementsByTagName("script");
for (var i = 0; i < scripts.length; i++) {
eval(scripts[i].innerText);
}
여기에서는 사용하지 않고 스크립트 , 링크 된 스크립트 및 모듈 과 함께 eval
작동 하는 솔루션입니다 .
이 함수는 3 개의 매개 변수를 허용합니다.
- html : 삽입 할 html 코드가 포함 된 문자열
- dest : 대상 요소에 대한 참조
- append : 대상 요소 html의 끝에 추가 할 수있는 부울 플래그
function insertHTML(html, dest, append=false){
// if no append is requested, clear the target element
if(!append) dest.innerHTML = '';
// create a temporary container and insert provided HTML code
let container = document.createElement('div');
container.innerHTML = html;
// cache a reference to all the scripts in the container
let scripts = container.querySelectorAll('script');
// get all child elements and clone them in the target element
let nodes = container.childNodes;
for( let i=0; i< nodes.length; i++) dest.appendChild( nodes[i].cloneNode(true) );
// force the found scripts to execute...
for( let i=0; i< scripts.length; i++){
let script = document.createElement('script');
script.type = scripts[i].type || 'text/javascript';
if( scripts[i].hasAttribute('src') ) script.src = scripts[i].src;
script.innerHTML = scripts[i].innerHTML;
document.head.appendChild(script);
document.head.removeChild(script);
}
// done!
return true;
}
예, 가능하지만 DOM 외부에서해야하며 순서가 맞아야합니다.
var scr = '<scr'+'ipt>alert("foo")</scr'+'ipt>';
window.onload = function(){
var n = document.createElement("div");
n.innerHTML = scr;
document.body.appendChild(n);
}
... 'foo'에게 경고합니다. 작동하지 않습니다.
document.getElementById("myDiv").innerHTML = scr;
And even this won't work, because the node is inserted first:
var scr = '<scr'+'ipt>alert("foo")</scr'+'ipt>';
window.onload = function(){
var n = document.createElement("div");
document.body.appendChild(n);
n.innerHTML = scr;
}
Gabriel Garcia's mention of MutationObservers is on the right track, but didn't quite work for me. I am not sure if that was because of a browser quirk or due to a mistake on my end, but the version that ended up working for me was the following:
document.addEventListener("DOMContentLoaded", function(event) {
var observer = new MutationObserver(mutations=>{
mutations.map(mutation=>{
Array.from(mutation.addedNodes).map(node=>{
if (node.tagName === "SCRIPT") {
var s = document.createElement("script");
s.text=node.text;
if (typeof(node.parentElement.added) === 'undefined')
node.parentElement.added = [];
node.parentElement.added[node.parentElement.added.length] = s;
node.parentElement.removeChild(node);
document.head.appendChild(s);
}
})
})
})
observer.observe(document.getElementById("element_to_watch"), {childList: true, subtree: true,attributes: false});
};
Of course, you should replace element_to_watch
with the name of the element that is being modified.
node.parentElement.added
is used to store the script tags that are added to document.head
. In the function used to load the external page, you can use something like the following to remove no longer relevant script tags:
function freeScripts(node){
if (node === null)
return;
if (typeof(node.added) === 'object') {
for (var script in node.added) {
document.head.removeChild(node.added[script]);
}
node.added = {};
}
for (var child in node.children) {
freeScripts(node.children[child]);
}
}
And an example of the beginning of a load function:
function load(url, id, replace) {
if (document.getElementById(id) === null) {
console.error("Element of ID "+id + " does not exist!");
return;
}
freeScripts(document.getElementById(id));
var xhttp = new XMLHttpRequest();
// proceed to load in the page and modify innerHTML
}
You can also wrap your <script>
like this and it will get executed:
<your target node>.innerHTML = '<iframe srcdoc="<script>alert(top.document.title);</script>"></iframe>';
Please note: The scope inside srcdoc
refers to the iframe, so you have to use top
like in the example above to access the parent document.
My solution for this problem is to set a Mutation Observer to detect <script></script>
nodes and then replace it with a new <script></script>
node with the same src. For example:
let parentNode = /* node to observe */ void 0
let observer = new MutationObserver(mutations=>{
mutations.map(mutation=>{
Array.from(mutation.addedNodes).map(node=>{
if ( node.parentNode == parentNode ) {
let scripts = node.getElementsByTagName('script')
Array.from(scripts).map(script=>{
let src = script.src
script = document.createElement('script')
script.src = src
return script
})
}
})
})
})
observer.observe(document.body, {childList: true, subtree: true});
Execute (Java Script) tag from innerHTML
Replace your script element with div having a class attribute class="javascript" and close it with </div>
Don't change the content that you want to execute (previously it was in script tag and now it is in div tag)
Add a style in your page...
<style type="text/css"> .javascript { display: none; } </style>
Now run eval using jquery(Jquery js should be already included)
$('.javascript').each(function() {
eval($(this).text());
});`
You can explore more here, at my blog.
참고URL : https://stackoverflow.com/questions/1197575/can-scripts-be-inserted-with-innerhtml
'Programing' 카테고리의 다른 글
원형 선분 충돌 탐지 알고리즘? (0) | 2020.05.14 |
---|---|
Visual Studio 2012/2013/2015/2017/2019에서 매크로를 기록 / 재생할 수 있습니까? (0) | 2020.05.14 |
힘내 재설정-하드 및 원격 저장소 (0) | 2020.05.13 |
콘다 환경의 이름을 바꾸려면 어떻게해야합니까? (0) | 2020.05.13 |
_ViewStart.cshtml 레이아웃 파일은 어디에 어떻게 연결되어 있습니까? (0) | 2020.05.13 |