Thực thi <script> được chèn bởi innerHTML sau lệnh gọi AJAX


99

Có một div được gọi là "Nội dung":

<div id="content"></div>

Nó phải chứa đầy dữ liệu từ một tệp PHP, của AJAX, bao gồm cả một <script>thẻ. Tuy nhiên, tập lệnh bên trong thẻ này không được thực thi.

<div id="content"><!-- After AJAX loads the stuff that goes here -->
   <script type="text/javascript">
     //code
   </script>
   <!-- More stuff that DOES work here -->
</div>

Bạn đang tải div như thế nào? tùy thuộc vào thư viện bạn sử dụng, thông thường bạn có thể kiểm soát xem bạn chỉ muốn thực thi các tập lệnh sau khi tải ajax.
Bill Yang

1
Sau khi window.onloadtôi tạo một XMTHttpRequestđối tượng để yêu cầu một trang (php) khác có chứa nội dung của div, bao gồm một tập lệnh. Tôi đang làm điều này với JS đơn giản, không có thư viện (trừ lol của riêng tôi)
JCOC611

Câu trả lời:


65

JavaScript được chèn dưới dạng văn bản DOM sẽ không thực thi. Tuy nhiên, bạn có thể sử dụng mẫu tập lệnh động để hoàn thành mục tiêu của mình. Ý tưởng cơ bản là di chuyển tập lệnh mà bạn muốn thực thi vào một tệp bên ngoài và tạo thẻ tập lệnh khi bạn nhận được phản hồi Ajax của mình. Sau đó, bạn đặt srcthuộc tính của thẻ tập lệnh của mình và thì nó sẽ tải và thực thi tập lệnh bên ngoài.

Bài đăng StackOverflow khác này cũng có thể hữu ích cho bạn: Có thể chèn tập lệnh bằng innerHTML không? .


bạn có thể chọn tất cả các kịch bản nạp và thực hiện chúng bằng cách eval () chức năng:$('#audit-view script').each(function (index, element) { eval(element.innerHTML); })
Jerzy Gebler

Javascript được thêm vào trang hoàn toàn nên thực thi. ( ví dụ: jsfiddle.net/wnn5fz3m/1 ). Câu hỏi là tại sao đôi khi nó không?
NoBugs

@Chocula Tôi đã chuyển tập lệnh của mình sang một tệp riêng biệt vẫn không hoạt động từ một phần, bạn có thể giúp gì với việc này không? stackoverflow.com/questions/42941856/…
Samra

FYI (@NoBugs cho tôi manh mối). Tôi đã thử javascirpt element.innerHTML = zzz đơn giản và không hoạt động. Sau đó, tôi thử Jquery $ (element) .html (zzz) và hoạt động.
gtryonp

66

Tôi đã sử dụng mã này, nó đang hoạt động tốt

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

4
Nếu bạn hỏi tôi, đây là câu trả lời tốt hơn nhiều so với câu được chấp nhận. Đây là khá nhiều JavaScript tiêm.
Xedret

Mặc dù hữu ích, nhưng điều này thực sự không trả lời câu hỏi tại sao các tập lệnh không chạy. Hãy xem ví dụ jsfiddle.net/fkqmcaz7jsfiddle.net/wnn5fz3m/1 Nó hoàn toàn nên chạy và tôi không tìm thấy trường hợp đơn giản nào về nơi nó không hoạt động.
NoBugs

1
@NaoiseGolden OP đang hỏi về việc thực thi tập lệnh theo đúng nghĩa đen, vì vậy tôi nghĩ rằng nó ổn ở đây. :-)
thedayturns

1
Eval () không được khuyến khích. Xem điều này - stackoverflow.com/questions/9107847/…
Kings

15

Nếu bạn tải một khối tập lệnh bên trong div của mình qua Ajax như sau:

<div id="content">
    <script type="text/javascript">
    function myFunction() {
      //do something
    }
    myFunction();
    </script>
</div>

... nó chỉ cập nhật DOM của trang của bạn, myFunction () không nhất thiết phải được gọi.

Bạn có thể sử dụng một phương thức gọi lại Ajax chẳng hạn như phương thức trong phương thức ajax () của jQuery để xác định những gì sẽ thực thi khi yêu cầu kết thúc.

Những gì bạn đang làm khác với việc tải một trang với JavaScript được bao gồm trong đó từ khi bắt đầu (được thực thi).

Ví dụ về cách sử dụng lệnh gọi lại thành công và lệnh gọi lại lỗi sau khi tìm nạp một số nội dung:

  $.ajax({
    type: 'GET',
    url: 'response.php',
    timeout: 2000,
    success: function(data) {
      $("#content").html(data);
      myFunction();
    },
    error: function (XMLHttpRequest, textStatus, errorThrown) {
      alert("error retrieving content");
    }

Một cách khác nhanh chóng và hiệu quả là sử dụng eval () để thực thi bất kỳ mã tập lệnh nào mà bạn đã chèn dưới dạng văn bản DOM nếu bạn không muốn sử dụng jQuery hoặc thư viện khác.


bằng cách sử dụng lệnh gọi lại của jQuery, làm cách nào tôi có thể "thực thi" mã bên trong <script> mới?
JCOC611

Tôi đã thêm một ví dụ về việc sử dụng lệnh gọi lại thành công:
Christopher Tokar

1
Cảm ơn bạn rất nhiều! "myFunction ();" một phần là những gì tôi đã bỏ qua mã của mình.
Jason

11

Đây là tập lệnh sẽ đánh giá tất cả các thẻ tập lệnh trong văn bản.

function evalJSFromHtml(html) {
  var newElement = document.createElement('div');
  newElement.innerHTML = html;

  var scripts = newElement.getElementsByTagName("script");
  for (var i = 0; i < scripts.length; ++i) {
    var script = scripts[i];
    eval(script.innerHTML);
  }
}

Chỉ cần gọi hàm này sau khi bạn nhận được HTML từ máy chủ. Được cảnh báo: sử dụng evalcó thể nguy hiểm.

Demo: http://plnkr.co/edit/LA7OPkRfAtgOhwcAnLrl?p=preview


5

Điều này 'chỉ hoạt động' đối với tôi khi sử dụng jQuery, miễn là bạn không cố gắng nối một tập hợp con HTML trả về XHR vào tài liệu. (Xem báo cáo lỗi này hiển thị sự cố với jQuery.)

Đây là một ví dụ cho thấy nó hoạt động:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 
<html lang="en"> 
<head> 
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 
    <title>test_1.4</title> 
    <script type="text/javascript" charset="utf-8" src="jquery.1.4.2.js"></script> 
    <script type="text/javascript" charset="utf-8"> 
        var snippet = "<div><span id='a'>JS did not run<\/span><script type='text/javascript'>" +
        "$('#a').html('Hooray! JS ran!');" +
        "<\/script><\/div>";
        $(function(){
            $('#replaceable').replaceWith($(snippet));
        });
    </script> 
</head> 
<body> 
    <div id="replaceable">I'm going away.</div> 
</body> 
</html>

Đây là tương đương với ở trên: http://jsfiddle.net/2CTLH/


1
Rất ít khả năng đây sẽ là giải pháp cho vấn đề hiện tại, vì đó là một lỗi với phiên bản jQuery thực sự cũ.
NoBugs

3

Đây là một hàm bạn có thể sử dụng để phân tích cú pháp các phản hồi AJAX, đặc biệt nếu bạn sử dụng minifiedjs và muốn nó thực thi Javascript trả về hoặc chỉ muốn phân tích cú pháp các đoạn mã mà không cần thêm chúng vào DOM, nó cũng xử lý các lỗi ngoại lệ. Tôi đã sử dụng mã này trong thư viện php4sack và nó hữu ích bên ngoài thư viện.

function parseScript(_source) {
    var source = _source;
    var scripts = new Array();

    // Strip out tags
    while(source.toLowerCase().indexOf("<script") > -1 || source.toLowerCase().indexOf("</script") > -1) {
        var s = source.toLowerCase().indexOf("<script");
        var s_e = source.indexOf(">", s);
        var e = source.toLowerCase().indexOf("</script", s);
        var e_e = source.indexOf(">", e);

        // Add to scripts array
        scripts.push(source.substring(s_e+1, e));
        // Strip from source
        source = source.substring(0, s) + source.substring(e_e+1);
    }

    // Loop through every script collected and eval it
    for(var i=0; i<scripts.length; i++) {
        try {
          if (scripts[i] != '')
          {         
            try  {          //IE
                  execScript(scripts[i]);   
      }
      catch(ex)           //Firefox
      {
        window.eval(scripts[i]);
      }   

            }  
        }
        catch(e) {
            // do what you want here when a script fails
         // window.alert('Script failed to run - '+scripts[i]);
          if (e instanceof SyntaxError) console.log (e.message+' - '+scripts[i]);
                    }
    }
// Return the cleaned source
    return source;
 }

2

Nếu bạn đang đưa một thứ gì đó cần thẻ script, bạn có thể gặp lỗi cú pháp không rõ ràng và nói mã thông báo bất hợp pháp . Để tránh điều này, hãy đảm bảo thoát khỏi các dấu gạch chéo trong (các) thẻ tập lệnh đóng của bạn. I E;

var output += '<\/script>';

Tương tự đối với bất kỳ thẻ đóng nào, chẳng hạn như thẻ biểu mẫu.


0

Tôi đã có một bài đăng tương tự ở đây, tải addEventListener trên tải ajax KHÔNG có jquery

Cách tôi đã giải quyết nó là chèn lời gọi đến các hàm trong hàm stateChange của tôi. Trang tôi đã thiết lập có 3 nút sẽ tải 3 trang khác nhau vào contentArea. Bởi vì tôi phải biết nút nào đang được nhấn để tải trang 1, 2 hoặc 3, tôi có thể dễ dàng sử dụng các câu lệnh if / else để xác định trang nào đang được tải và sau đó chạy chức năng nào. Những gì tôi đang cố gắng làm là đăng ký các trình nghe nút khác nhau sẽ chỉ hoạt động khi trang cụ thể được tải vì ID phần tử ..

vì thế...

if (page1 đang được tải, pageload = 1) chạy hàm registerListists1

sau đó tương tự cho trang 2 hoặc 3.


0

Điều này đã làm việc cho tôi bằng cách gọi eval trên mỗi nội dung tập lệnh từ ajax .done:

$.ajax({}).done(function (data) {      
    $('div#content script').each(function (index, element) { eval(element.innerHTML); 
})  

Lưu ý: Tôi không ghi các tham số vào $ .ajax mà bạn phải điều chỉnh theo ajax của mình.


0

Kết luận của tôi là HTML không cho phép các thẻ NESTED SCRIPT. Nếu bạn đang sử dụng javascript để chèn mã HTML bao gồm các thẻ script bên trong sẽ không hoạt động vì javascript cũng đi trong một thẻ script. Bạn có thể kiểm tra nó với mã tiếp theo và bạn sẽ biết rằng nó sẽ không hoạt động. Trường hợp sử dụng là bạn đang gọi một dịch vụ với AJAX hoặc tương tự, bạn đang nhận được HTML và bạn muốn đưa nó vào HTML DOM ngay lập tức. Nếu mã HTML được chèn bên trong các thẻ SCRIPT sẽ không hoạt động.

<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"></head><body></body><script>document.getElementsByTagName("body")[0].innerHTML = "<script>console.log('hi there')</script>\n<div>hello world</div>\n"</script></html>

-3

Một việc khác cần làm là tải trang bằng một tập lệnh như:

<div id="content" onmouseover='myFunction();$(this).prop( 'onmouseover', null );'>
<script type="text/javascript">
function myFunction() {
  //do something
}
myFunction();
</script>
</div>

Thao tác này sẽ tải trang, sau đó chạy tập lệnh và xóa trình xử lý sự kiện khi chức năng đã được chạy. Điều này sẽ không chạy ngay sau khi tải ajax, nhưng nếu bạn đang đợi người dùng nhập phần tử div, thì điều này sẽ hoạt động tốt.

Tái bút. Yêu cầu Jquery

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.