สามารถแทรกสคริปต์ด้วย innerHTML ได้หรือไม่


223

ผมพยายามที่จะโหลดสคริปต์บางอย่างในหน้าโดยใช้บน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>

คำตอบ:


88

คุณต้องใช้eval ()เพื่อรันโค้ดสคริปต์ใด ๆ ที่คุณแทรกเป็นข้อความ DOM

MooTools จะทำสิ่งนี้ให้คุณโดยอัตโนมัติและฉันมั่นใจว่า jQuery ก็เช่นกัน (ขึ้นอยู่กับรุ่น jQuery เวอร์ชัน 1.6+ ใช้eval) วิธีนี้ช่วยลดความยุ่งยากในการแยกวิเคราะห์<script>แท็กและหลีกเลี่ยงเนื้อหาของคุณเช่นเดียวกับ "gotchas" อื่น ๆ

โดยทั่วไปถ้าคุณจะไปeval()ด้วยตัวคุณเองคุณต้องการสร้าง / ส่งรหัสสคริปต์โดยไม่ต้องมาร์กอัป HTML เช่น<script>นี้จะไม่eval()ถูกต้อง


12
สิ่งที่ฉันต้องการทำจริงๆคือการโหลดสคริปต์ภายนอกไม่ใช่เพียงแค่ทำการวิเคราะห์สคริปต์ท้องถิ่น การเพิ่มแท็กสคริปต์ด้วย innerHTML นั้นสั้นกว่าการสร้างองค์ประกอบ DOM ของสคริปต์และเพิ่มลงในเนื้อหาและฉันพยายามทำให้รหัสของฉันสั้นที่สุด คุณต้องสร้างองค์ประกอบสคริปต์ dom และเพิ่มลงใน dom แทนที่จะใช้บางอย่างเช่น innerHTML หรือไม่ มีวิธีการทำสิ่งนี้กับ document.write จากภายในฟังก์ชั่นหรือไม่?
เครก

5
อย่างที่ซอมบี้แนะนำให้ใช้เฟรมเวิร์ก Javascript เพื่อโหลดสคริปต์ภายนอกอย่าพยายามหมุนล้อใหม่ JQuery ทำให้ง่ายมากเพียงแค่รวม JQuery และการโทร: $ .getScript (url) คุณยังสามารถจัดเตรียมฟังก์ชันการเรียกกลับที่จะถูกเรียกใช้งานได้เมื่อโหลดสคริปต์แล้ว
Ariel Popovsky

2
เอเรียลถูกต้อง ฉันขอขอบคุณที่พยายามทำให้โค้ดของคุณสั้นและการเพิ่ม<script>แท็กด้วยinnerHTMLอาจสั้น แต่ก็ใช้ไม่ได้ eval()มันคือทั้งหมดที่ข้อความธรรมดาเพียงจนกว่าจะได้รับวิ่งผ่าน และน่าเศร้าที่eval()ไม่ต้องแยกแท็ก HTML ดังนั้นคุณจะพบกับปัญหามากมาย
zombat

21
eval () ไม่ใช่วิธีแก้ไขปัญหาใด ๆ
buley

2
ฉันลอง eval () ด้วยตัวเอง มันเป็นความคิดที่น่ากลัว คุณต้อง eval สิ่งที่ทั้งแต่ละครั้ง แม้ว่าคุณจะประกาศชื่อตัวแปรและค่าคุณจะต้องประกาศใหม่อีกครั้ง / eval () ทุกครั้งที่มันทำงานอีกครั้ง มันเป็นฝันร้ายของข้อผิดพลาด
Youstay Igo

88

นี่คือทางออกที่น่าสนใจสำหรับปัญหาของคุณ: http://24ways.org/2005/have-your-dom-and-script-it-too

ดังนั้นใช้สิ่งนี้แทนแท็กสคริปต์:

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


12
มันยอดเยี่ยมมาก!
confile

ไม่ทำงานสำหรับฉันเมื่อแทรกลงในผลลัพธ์ของคำขอ ajax: ข้อผิดพลาดทางไวยากรณ์หายไป; ก่อนที่จะแถลงในตอนเริ่มต้นของสตริงสคริปต์
โอลิเวอร์

ผู้คนเพิ่มบรรทัด & lt; img src ... ในโค้ดเพจของคุณอย่างไร คุณ document.write () มันหรือใช้ document.body.innerHTML + = แนวทางสำหรับการที่? ทั้งสองล้มเหลวสำหรับฉัน :(
Youstay Igo

ไม่ค่อยมีประโยชน์ในการเขียนโค้ดจำนวนมากภายในonloadแอตทริบิวต์ นอกจากนี้ต้องมีไฟล์เพิ่มเติมที่จะมีอยู่และจะโหลด วิธีการแก้ปัญหาของ momoน้อยกว่าการประนีประนอม
Fregante

15
คุณสามารถ Base64 เข้ารหัสภาพทริกเกอร์ของคุณเป็น<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">(นี้จะไม่ทำคำขอเครือข่าย) ที่จริง ... คุณไม่จำเป็นต้องมีภาพอ้างอิงภาพที่ไม่มีอยู่และแทนที่จะonloadใช้onerror(แต่จะทำคำขอเครือข่าย)
แดนนี่ '365CSI' Engelman

83

นี่คือวิธีที่จะแทนที่สคริปต์ทั้งหมดด้วยสคริปต์ที่เรียกใช้ซ้ำ:

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

9
ฉันรู้สึกประหลาดใจเล็กน้อยที่คำตอบของคุณจบลงแล้ว IMHO นี่เป็นทางออกที่ดีที่สุดวิธีนี้จะช่วยให้คุณสามารถ จำกัด สคริปต์ด้วย URL หรือเนื้อหาที่เฉพาะเจาะจง
davidmh

1
@ inf3rno ทำหรือไม่ทำ? มันเคยทำงานใครเคยอ้างว่ามีอะไรแปลกใหม่?
mmm

purpoes ของ [0] คืออะไร? คุณสามารถใช้ nodeScriptReplace (document.getElementById (). html);
เปาไทย

@BaoThai ใช่ คุณสามารถ.
mmm

ดูเหมือนจะไม่ช่วยใน IWebBrowser2; ฉันสามารถยืนยันว่าแท็กสคริปต์ถูกสร้างขึ้นใหม่ด้วย createElement แต่ฉันยังคงไม่สามารถเรียกใช้แท็กสคริปต์เหล่านี้ผ่าน InvokeScript ()
เดฟ

46

คุณสามารถสร้างสคริปต์แล้วฉีดเนื้อหา

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

สิ่งนี้ใช้ได้กับเบราว์เซอร์ทั้งหมด :)


1
เว้นแต่จะไม่มีองค์ประกอบสคริปต์อื่น ๆ ในเอกสาร ใช้document.documentElementแทน
Eli Gray

4
ไม่จำเป็นเพราะคุณกำลังเขียนสคริปต์จากสคริปต์อื่น <script> var g = document.createElement('script'); var s = document.getElementsByTagName('script')[0]; //reference this script g.text = "alert(\"hi\");" s.parentNode.insertBefore(g, s); </script>
Pablo Moretti

3
ใครบอกว่ามาจากสคริปต์อื่น คุณสามารถเรียกใช้ JavaScript โดยไม่มี<script>องค์ประกอบ เช่นและ<img onerror="..." src="#"> <body onload="...">หากคุณต้องการเป็นเทคนิคสิ่งนี้จะไม่ทำงานในเอกสารที่ไม่ใช่ HTML / SVG เนื่องจากการกำหนดเนมสเปซที่ไม่ได้อธิบาย
อีไลเกรย์

2
Facebook ใช้คำตอบของ Pablo ใน SDK ของพวกเขา developers.facebook.com/docs/javascript/quickstart/v2.2#loading
geoyws

30

ฉันใช้รหัสนี้มันใช้งานได้ดี

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

1
ขอบคุณ มันแก้ไขปัญหาของฉันในการเพิ่มรหัส Disqus Universal ลงในป๊อปอัป modal ที่สร้างโดยใช้ปลั๊กอิน TinyBox2 Jquery
gsinha

3
น่าเสียดายที่โซลูชันนี้ใช้ไม่ได้เมื่อสคริปต์มีฟังก์ชันที่จะเรียกใช้ในภายหลัง
Jose Gómez

7

ฉันมีปัญหานี้กับ innerHTML ฉันต้องผนวกสคริปต์ Hotjar ต่อท้าย "แท็ก" ของแอปพลิเคชัน Reactj ของฉันและจะต้องดำเนินการทันทีหลังจากต่อท้าย

หนึ่งในโซลูชั่นที่ดีสำหรับการนำเข้าโหนดแบบไดนามิกในแท็ก "หัว" คือโมดูลตอบสนอง


นอกจากนี้ยังมีวิธีแก้ปัญหาที่มีประโยชน์สำหรับปัญหาที่เสนอ:

ไม่มีแท็กสคริปต์ใน innerHTML!

ปรากฎว่า HTML5 ไม่อนุญาตให้เพิ่มแท็กสคริปต์แบบไดนามิกโดยใช้คุณสมบัติ InnerHTML ดังนั้นสิ่งต่อไปนี้จะไม่ทำงานและจะไม่มีการเตือนว่า Hello World!

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

เอกสารนี้มีข้อกำหนดใน HTML5:

หมายเหตุ: องค์ประกอบสคริปต์ที่แทรกโดยใช้ innerHTML จะไม่ทำงานเมื่อมีการแทรก

แต่ระวังสิ่งนี้ไม่ได้หมายความว่า innerHTML ปลอดภัยจากการเขียนสคริปต์ข้ามไซต์ มันเป็นไปได้ที่จะดำเนินการผ่านทาง JavaScript innerHTML โดยไม่ต้องใช้แท็กที่แสดงบนหน้า innerHTML MDN ของ

การแก้ไข: การเพิ่มสคริปต์แบบไดนามิก

ในการเพิ่มแท็กสคริปต์แบบไดนามิกคุณต้องสร้างองค์ประกอบสคริปต์ใหม่และผนวกเข้ากับองค์ประกอบเป้าหมาย

คุณสามารถทำได้สำหรับสคริปต์ภายนอก:

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

5

สำหรับทุกคนที่ยังคงพยายามที่จะทำเช่นนี้ไม่มีคุณไม่สามารถฉีดสคริปต์ใช้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)
    });
}

ฉันทำให้สิ่งนี้ง่ายขึ้นจากการใช้งานจริงของฉันดังนั้นจึงไม่มีสัญญาว่าจะไม่มีข้อบกพร่องใด ๆ แต่หลักการทำงาน

หากคุณไม่สนใจที่จะรับค่าใด ๆ กลับมาหลังจากที่สคริปต์ทำงานได้ เพียงแค่ปล่อยPromiseและonloadบิต คุณไม่จำเป็นต้องปิดสคริปต์หรือสร้างwindow.__load_script_exports_คุณสมบัติโกลบอล


1
ฉันลองแล้วมันใช้งานได้กับ chrome 57. InnerHTML บนแท็กสคริปต์เรียกใช้งานข้อความ
iPherian

นั่นน่าสนใจมันไม่ได้ทำงานมาก่อน ฉันสงสัยว่าพฤติกรรมนี้เป็นแบบข้ามเบราว์เซอร์หรือเฉพาะในโครเมี่ยม 57
JayArby

4

นี่คือฟังก์ชันเรียกซ้ำเพื่อตั้งค่า 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);
    }
}

คุณสามารถดูการสาธิตที่นี่


3

คุณสามารถทำสิ่งนี้ได้:

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

3

นี่คือวิธีการแก้ปัญหาที่ไม่ได้ใช้evalและทำงานร่วมกับสคริปต์ , สคริปที่เชื่อมโยงเช่นเดียวกับโมดูล

ฟังก์ชันรับพารามิเตอร์ 3 ตัว:

  • html : สตริงที่มีรหัส html ที่จะแทรก
  • ปลายทาง : อ้างอิงถึงองค์ประกอบเป้าหมาย
  • ผนวก : แฟล็กบูลีนเพื่อเปิดใช้งานการผนวกที่ส่วนท้ายขององค์ประกอบเป้าหมาย 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;
}

ฉันหมายถึง ... การผนวกแท็กสคริปต์ที่มีเนื้อหารหัสเป็นสิ่งที่ประเมินได้
Kevin B

@KevinB มีความแตกต่างฉาวโฉ่ ... ลองeval('console.log(this)')แล้วคุณจะเห็นชัดเจนที่สุด
colxi

บริบทจึงแตกต่างกันและ มันยังคงเป็นแค่หลักฐาน
Kevin B

@KevinB ไม่มันไม่ใช่การพิสูจน์ ลองนี้eval('let b=100').. แล้วลองเข้าถึง bจากภายนอก eval .... ขอให้โชคดีด้วยคุณจะต้องใช้มัน
colxi

ได้ผลสำหรับฉัน ไชโย
Bezzzo

1

Krasimir Tsonev มีทางออกที่ยอดเยี่ยมที่เอาชนะปัญหาทั้งหมด วิธีการของเขาไม่ต้องการใช้ eval ดังนั้นจึงไม่มีปัญหาเรื่องประสิทธิภาพและความปลอดภัย อนุญาตให้คุณตั้งค่าสตริง innerHTML มี html พร้อม js และแปลเป็นองค์ประกอบ DOM ทันทีในขณะที่ยังดำเนินการส่วน js ที่มีอยู่ตามรหัส สั้นง่ายและทำงานตรงตามที่คุณต้องการ

สนุกกับการแก้ปัญหาของเขา:

http://krasimirtsonev.com/blog/article/Convert-HTML-string-to-DOM-element

หมายเหตุสำคัญ:

  1. คุณต้องล้อมองค์ประกอบเป้าหมายด้วยแท็ก div
  2. คุณต้องตัดสตริง src ด้วยแท็ก div
  3. หากคุณเขียนสตริง src โดยตรงและมีชิ้นส่วน js โปรดระวังในการเขียนแท็กสคริปต์การปิดอย่างถูกต้อง (ด้วย \ before /) เนื่องจากนี่คือสตริง

1

ใช้แทน$(parent).html(code)parent.innerHTML = code

ต่อไปนี้ยังแก้ไขสคริปต์ที่ใช้document.writeและสคริปต์ที่โหลดผ่านsrcแอตทริบิวต์ น่าเสียดายที่สิ่งนี้ไม่สามารถใช้ได้กับสคริปต์ Google AdSense

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

แหล่ง


1
สายไปหน่อย แต่ใครก็ตามที่อาจใช้วิธีนี้สังเกตว่าใน JQuery คุณต้องโหลดสคริปต์โดยใช้ $ .loadScript (url) แทนที่จะเป็น <script src = "url> </script> - หลังจะทำให้ เลิกใช้ข้อผิดพลาด Synchronous XMLHttpRequest บนเบราว์เซอร์
Stavm

1

ลองใช้เทมเพลตและ 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>


1
สิ่งนี้ใช้ไม่ได้กับ Microsoft Edge หรือวิธีแก้ไขอื่นใด
Soul

1

คุณยังสามารถห่อ<script>สิ่งนี้ได้และจะถูกดำเนินการ:

<your target node>.innerHTML = '<iframe srcdoc="<script>alert(top.document.title);</script>"></iframe>';

โปรดทราบ: ขอบเขตภายในsrcdocอ้างอิงถึง iframe ดังนั้นคุณต้องใช้topเช่นในตัวอย่างด้านบนเพื่อเข้าถึงเอกสารหลัก


1

ฉันทำสิ่งนี้ทุกครั้งที่ฉันต้องการแทรกแท็กสคริปต์แบบไดนามิก!

  const html =
    `<script>
        alert('👋 there ! Wanna grab a 🍺'); 
    </script>`;

  const scriptEl = document.createRange().createContextualFragment(html);
  parent.append(scriptEl);

หมายเหตุ: ใช้ ES6

แก้ไข 1: ความชัดเจนสำหรับพวกคุณ - ฉันเห็นคำตอบมากมายที่ใช้appendChildและต้องการให้พวกคุณรู้ว่ามันใช้งานได้ดีappend


0

ใช่คุณทำได้ แต่คุณต้องทำนอก 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;

และแม้กระทั่งสิ่งนี้จะไม่ทำงานเพราะโหนดถูกแทรกก่อน

var scr = '<scr'+'ipt>alert("foo")</scr'+'ipt>';
window.onload = function(){
    var n = document.createElement("div");
    document.body.appendChild(n);
    n.innerHTML = scr;  
}

16
สำหรับสิ่งที่มีค่า: สิ่งนี้ดูเหมือนจะไม่ทำงานบนเบราว์เซอร์ปัจจุบัน
Wichert Akkerman

0

โซลูชันของฉันสำหรับปัญหานี้คือการตั้งค่าตัวสังเกตการกลายพันธุ์เพื่อตรวจจับ<script></script>โหนดและแทนที่ด้วย<script></script>โหนดใหม่ด้วย src เดียวกัน ตัวอย่างเช่น:

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

1
ขอบคุณสำหรับ downvoting ฉันโดยไม่บอกว่าทำไม รักทุกคน
กาเบรียลการ์เซี

0

กาเบรียลการ์เซียพูดถึง MutationObservers อยู่ในเส้นทางที่ถูกต้อง แต่ก็ไม่ได้ผลสำหรับฉัน ฉันไม่แน่ใจว่าเป็นเพราะการเล่นโวหารของเบราว์เซอร์หรือเนื่องจากข้อผิดพลาดในตอนท้ายของฉัน แต่เวอร์ชันที่จบลงด้วยการทำงานสำหรับฉันนั้นเป็นดังนี้:

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

แน่นอนคุณควรแทนที่ element_to_watchด้วยชื่อขององค์ประกอบที่จะถูกแก้ไข

node.parentElement.addeddocument.headถูกนำมาใช้ในการจัดเก็บแท็กสคริปต์ที่จะมีการเพิ่ม ในฟังก์ชันที่ใช้ในการโหลดหน้าภายนอกคุณสามารถใช้สิ่งต่อไปนี้เพื่อลบแท็กสคริปต์ที่ไม่เกี่ยวข้องอีกต่อไป:

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

และตัวอย่างของการเริ่มต้นของฟังก์ชั่นโหลด:

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
}

คุณสังเกตเห็นว่าคุณกำลังเพิ่มองค์ประกอบใหม่MutationObserverทุกครั้งที่องค์ประกอบถูกผนวกเข้ากับเอกสารใช่ไหม Btw ฉันสงสัยว่าทำไมคุณถึงบอกว่ารหัสของฉันไม่ทำงาน
กาเบรียลการ์

@gabrielgarcia ฉันบอกว่ารหัสของคุณใช้งานไม่ได้เพราะฉันลองแล้วมันก็ใช้ไม่ได้ มองไปที่มันเป็นไปได้ทั้งหมดที่อยู่กับฉันไม่ใช่คุณและฉันขอโทษอย่างจริงใจสำหรับวิธีที่ฉันใช้ถ้อยคำนี้ แก้ไขได้ทันที
pixelherodev

Re: การเพิ่ม MutationObserver ทุกครั้งที่มีการเพิ่มองค์ประกอบลงในเอกสารคุณกำลังพูดถึงอะไร DOMContentLoaded และฉันเสนอราคาจาก MDN ที่นี่ "ไฟไหม้เมื่อเอกสาร HTML เริ่มต้นได้รับการโหลดอย่างสมบูรณ์และแยกวิเคราะห์โดยไม่ต้องรอสไตล์ชีตรูปภาพและเฟรมย่อยเพื่อโหลดเสร็จ" นั่นคือครั้งเดียวและครั้งเดียวเท่านั้น นอกจากนี้สคริปต์นี้ทำงานได้โดยไม่มีปัญหาในเว็บไซต์ของฉันและการดีบักแสดงให้เห็นว่าเกิดขึ้นเพียงครั้งเดียวดังนั้นจึงเป็นการปฏิบัติครั้งเดียวและในทางทฤษฎี
pixelherodev

1
ถูกแล้ว ... ฉันเข้าใจผิด ฉันขอโทษด้วย
กาเบรียลการ์

@gabrielgarcia ไม่ใช่ปัญหา :)
pixelherodev

0

สำหรับฉันวิธีที่ดีที่สุดคือการแทรกเนื้อหา HTML ใหม่ผ่าน innerHtml แล้วใช้

setTimeout(() => {
        var script_el = document.createElement("script")
        script_el.src = 'script-to-add.js'
        document.body.appendChild(script_el)
    }, 500)

ไม่จำเป็นต้องใช้ setTimeout แต่ทำงานได้ดีขึ้น สิ่งนี้ใช้ได้สำหรับฉัน


-1

เรียกใช้แท็ก (Java Script) จาก innerHTML

แทนที่องค์ประกอบสคริปต์ของคุณด้วย div ที่มี class attribute class = "javascript" และปิดด้วย </div>

อย่าเปลี่ยนเนื้อหาที่คุณต้องการดำเนินการ (ก่อนหน้านี้มันอยู่ในแท็กสคริปต์และตอนนี้มันอยู่ในแท็ก div)

เพิ่มสไตล์ในหน้าของคุณ ...

<style type="text/css"> .javascript { display: none; } </style>

ตอนนี้เรียกใช้ eval โดยใช้ jquery (ควร jquery js อยู่แล้ว)

   $('.javascript').each(function() {
      eval($(this).text());

    });`

คุณสามารถสำรวจเพิ่มเติมได้ที่นี่ที่บล็อกของฉัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.