2011-01-18 33 views
24

Đây là một chút của một trường hợp sử dụng kỳ quặc, nhưng tôi có lý do của tôi:Sử dụng JavaScript để ngăn không cho thẻ `<script>` sau bị đánh giá?

Tôi muốn để có thể viết

<script type="text/javascript" src="first.js"></script> 
<script type="text/javascript" src="second.js"></script> 

trong đánh dấu của tôi và, bằng cách sử dụng mã trong first.js, ngăn chặn hoặc trì hoãn việc thực hiện second.js. Điều này có thể, trong bất kỳ trình duyệt nào không? Điều gì xảy ra nếu nội dung của first.js được nội tuyến? (. Nếu nó giúp, giả sử rằng thẻ kịch bản thứ hai có một thuộc tính id)

Kể từ khi tôi đã nhận được một vài câu trả lời mà bỏ lỡ những gì tôi nhận được lúc, tôi nên làm rõ:

  1. Các giải pháp phải hoàn toàn trong phạm vi first.js. Bất kỳ điều gì yêu cầu thay đổi đối với HTML gốc của trang hoặc đến second.js, đều không được chấp nhận.
  2. chấp nhận được để tải second.js qua Ajax và thực hiện nó bằng cách sử dụng eval. Đó là phần dễ dàng. Phần khó khăn là ngăn chặn việc thực hiện ngay lập tức second.js.
  3. Giả sử rằng bạn không biết những gì có trong second.js. Vì vậy, bạn không thể chỉ thay thế từng chức năng toàn cầu được gọi là second.js với chức năng no-op. (Ngoài ra, điều này gần như chắc chắn sẽ dẫn đến lỗi.)

Nếu bạn biết một giải pháp hoạt động trong một số trình duyệt nhưng không phải ở các trình duyệt khác, tôi rất muốn nghe nó.

Ví dụ: Để thực hiện cụ thể hơn này một chút, chúng ta hãy nói rằng mã

<script type="text/javascript"> 
    function func() { 
    window.meaningOfLife = 42; 
    window.loadSecond(); 
    }; 
    setTimeout(func, 10); 
</script> 

trước hai script bao gồm, và rằng second.js chứa dòng

if (window.meaningOfLife !== 42) {throw new Error();} 

first.js nên có thể ngăn chặn lỗi này bằng cách trì hoãn second.js thực hiện cho đến khi window.loadSecond được chạy. (Giả sử việc thực hiện window.loadSecond cũng có trong first.js.) Đây là không được phép chạm vào window.meaningOfLife.

Cập nhật: Câu trả lời của Alohci đáp ứng các yêu cầu này, nhưng chỉ với điều kiện là các thẻ script thứ hai đến ngay sau khi người đầu tiên, không có gì nhưng khoảng trắng ở giữa. Nếu ai đó có thể mở rộng hack của mình để tránh yêu cầu đó, mà không đưa ra những hậu quả không mong muốn khác, điều đó thật tuyệt vời ...

Trả lời

24

Với các yêu cầu cụ thể của bạn, điều này thực sự khá đơn giản và nên hoạt động hoàn toàn trên trình duyệt. Tuy nhiên, nó yêu cầu first.js đứng trước second.js mà không có bất kỳ thứ gì giữa chúng ngoại trừ khoảng trắng.

Đầu tiên, chúng ta hãy giả định rằng HTML trông như thế này:

<!DOCTYPE html> 
<html> 
    <head> 
    <title>Test Case</title> 
    <meta charset="UTF-8" /> 
    <script type="text/javascript"> 
     function func() { 
     window.meaningOfLife = 42; 
     window.loadSecond(); 
     }; 
    </script> 
    <script type="text/javascript" src="first.js"></script> 
    <script type="text/javascript" src="second.js"></script> 
    </head> 
    <body> 
    <p>Lorem ipsum dolor sit amet ...</p> 
    <a href="javascript:func()">Run Func()</a> 
    </body> 
</html> 

Tôi đã gỡ bỏ các setTimeout vì có thể gây func() để chạy trước start.js chạy gây ra một "loadSecond không được định nghĩa" lỗi. Thay vào đó, tôi đã cung cấp một neo để được bấm vào để chạy func().

Thứ hai, chúng ta hãy giả sử rằng second.js trông như thế này:

document.body.appendChild(document.createTextNode("second.js has run. ")); 
if (window.meaningOfLife !== 42) {throw new Error();} 

Ở đây, tôi vừa bổ sung thêm một dòng để thêm một số văn bản cho cơ thể tài liệu, vì vậy nó dễ dàng hơn để xem khi thứ hai .js thực sự chạy.

Sau đó, giải pháp cho first.js là thế này:

function loadSecond() 
{ 
    var runSecond = document.createElement("script"); 
    runSecond.setAttribute("src", "second.js"); 
    document.body.appendChild(runSecond); 
} 

document.write("<script type='application/x-suppress'>"); 

Chức năng loadSecond chỉ là có để chạy second.js khi func() chạy.

Chìa khóa để giải pháp là dòng document.write. Nó sẽ chèn vào HTML <script type='application/x-suppress'> giữa thẻ tập lệnh đóng của first.js và thẻ tập lệnh mở của second.js.

Trình phân tích cú pháp sẽ thấy điều này và bắt đầu phần tử tập lệnh mới. Bởi vì thuộc tính type có một giá trị không phải là một giá trị mà trình duyệt nhận ra là JavaScript, nên nó sẽ không cố gắng chạy nội dung của nó. (Vì vậy, có vô số giá trị thuộc tính type có thể bạn có thể sử dụng ở đây, nhưng bạn phải bao gồm thuộc tính type, như trong trường hợp không có, trình duyệt sẽ giả định rằng nội dung của tập lệnh là JavaScript.)

Second.js thẻ mở của tập lệnh sau đó sẽ được phân tích cú pháp dưới dạng nội dung văn bản của phần tử tập lệnh mới và không được thực thi. Cuối cùng, thẻ đóng của tập lệnh thứ hai.js sẽ được tái định nghĩa để đóng phần tử kịch bản mới thay vào đó, điều đó có nghĩa là phần còn lại của HTML được phân tích cú pháp chính xác.

Bạn có thể thấy một phiên bản làm việc tại http://www.alohci.net/static/jsprevent/jsprevent.htm

+5

@ Ms2ger, điều đó hoàn toàn không liên quan đến câu hỏi ... Alohci, câu trả lời hay! –

+1

Đây là một hack tẻ nhạt, nhưng nó có vẻ là cảnh quay tốt nhất vì thiếu một cơ chế thực sự để làm điều này. Bất cứ ai làm việc sử dụng điều này nên nhận thức được rằng nó là một hack dễ vỡ và xấu xí mà các trình duyệt có thể chọn để đè bẹp bất cứ lúc nào. Nếu điều này không thực thi của một kịch bản thực sự là nhiệm vụ quan trọng, tôi đề nghị làm việc với các tác giả ban đầu của html và các tệp js khác. – Caleb

+0

Họ nói rằng nó không thể được thực hiện, nhưng bạn đã thực hiện nó! Trong khi tôi đồng ý rằng điều này có lẽ không nên được sử dụng trong sản xuất, nó hoạt động mà không có một hitch trong mọi trình duyệt tôi đã thử nó. Congrats, Alohci, tiền thưởng là của bạn. :) –

-2

bạn có thể sử dụng setTimeout() để trì hoãn việc thực hiện một số mã

+1

Đúng vậy, nhưng tôi hỏi: Nếu tôi có thể kiểm soát first.js, và không second.js? –

6

Trong first.js, thiết var shouldILoad = true

Sau đó, tải second.js theo cách này:

<script> 
    if (shouldILoad) { 
     (function() { 
      var myscript = document.createElement('script'); 
      myscript.type = 'text/javascript'; 
      myscript.src = ('second.js'); 
      var s = document.getElementById('myscript'); 
      s.parentNode.insertBefore(myscript, s); 
     })(); 
    } 
</script> 

(nơi 'myscript' là ID của một số yếu tố trước đó bạn muốn chèn phần tử Tập lệnh mới)

+2

Xin lỗi, bản dịch gốc của tôi có thể không rõ ràng: Giả sử rằng tôi không có quyền kiểm soát HTML gốc của trang; Tôi chỉ kiểm soát nội dung của first.js. –

+0

Ah, trong trường hợp đó tôi không có đầu mối. Tôi sẽ để lại câu trả lời của tôi, vì đây vẫn là một kỹ thuật hữu ích nếu bạn có quyền truy cập đó :) –

4

Theo như tôi biết, bạn có thể 't. Nếu đánh dấu trông giống như

<script type="text/javascript" src="first.js"></script> 
<script type="text/javascript" src="second.js"></script> 

bạn không thể truy cập vào các yếu tố kịch bản thứ hai từ bên trong first.js, vì nó đã không được thêm vào DOM tại thời điểm kịch bản đầu tiên chạy (thậm chí không nếu bạn gán một id thành phần tử thứ hai). Việc mã số second.js có được đặt nội tuyến hay trong tệp bên ngoài không quan trọng.

Điều duy nhất tôi không hiểu là điểm thứ hai của bạn. Trước tiên, bạn nói rằng bạn không thể kiểm soát đánh dấu của tài liệu, nhưng sau đó bạn nói rằng nó có thể tải second.js động (sử dụng AJAX).

+0

Tại sao điều này lại bị bỏ phiếu? Tôi hy vọng không phải vì nó không phải là câu trả lời mà OP hy vọng. Vui lòng giải thích. –

+0

Đúng vậy, tôi đồng ý rằng điều đó dường như là không thể ... nhưng, galambalaz có một giải pháp hoạt động trong một số trường hợp. :) Điểm đầu tiên của tôi là bạn không có quyền kiểm soát đánh dấu * gốc * của tài liệu; bạn chỉ có thể thao tác nó từ 'first.js'. Tôi đã trả lời giải pháp của dorkitude, nó chỉ hoạt động nếu bạn bỏ qua thẻ '

0

Chỉ để xem điều này có khả thi không, tôi có first.js gửi đồng bộ XHR vào tệp PHP và đã xóa tệp PHP second.js. Khi readyState đạt đến '4', tôi có cảnh báo JS, để dừng luồng. Sau đó, tôi đã đi và kiểm tra máy chủ ... Yeah, second.js đã bị xóa. Tuy nhiên, nó sẽ không hoạt động. Tôi sẽ đóng hộp cảnh báo và mã được trongsecond.js vẫn sẽ được thực thi, mặc dù tệp đã biến mất.

Tôi thực sự không biết điều này có nghĩa là gì, nhưng câu trả lời cho câu hỏi của bạn có lẽ là "Không, không thể."

+0

Cả hai tệp JavaScript có thể được tải * cùng một lúc, vì vậy bạn có thể xóa 'second.js' sau khi nó đã được gửi hoàn toàn tới trình duyệt. –

+0

Đó là một phương pháp thú vị. Nhưng yeah, tôi không ngạc nhiên khi nó không hoạt động.Và giả sử rằng chúng ta không được phép thao túng máy chủ, phải không? –

2

Tất cả các thẻ <script>ngữ cảnh thực thi riêng của mình, điều này làm cho hầu như không thể can thiệp lẫn nhau. Tất nhiên bạn đã có đối tượng toàn cầu (khét tiếng) (được tham chiếu bởi window trong trình duyệt).

Ngăn chặn việc thực thi second.js khá đơn giản: ngắt! Giả sử rằng second.js cố gắng gọi số document.getElementById ví dụ:

Working example phá vỡ jQuery, sau đó tải sau (với phụ thuộc).
Tested on: IE 6 +, FF 3.6+, Chrome

cuối first.js

var execute; 

// saving our position 
var scripts = document.getElementsByTagName("script"); 
var i = scripts.length; 

// breaking getElementById 
var byId = document.getElementById; 
document.getElementById = null; 

var interval = setInterval(function() { 
    if (i != scripts.length) { 
     var second = scripts[i]; 
     // stop polling 
     clearInterval(interval); 
     // fix getElementById 
     document.getElementById = byId; 
     // set the delayed callback 
     execute = function (onload) { 
     var script = document.createElement("script"); 
     script.src = second.src; 
     script.onload = script.onreadystatechange = onload; 
     document.getElementsByTagName("head")[0].appendChild(script); 
     }; 
    } 
}, 100); 

bất cứ lúc nào bạn muốn thực hiện second.js

execute(function(){ 
    // second.js dependant code goes here... 
}); 

Lưu ý: thông số onload cho execute là tùy chọn al.

+0

Mặc dù tôi cũng nên lưu ý rằng toàn bộ ý tưởng là khá ma quái. :) – galambalazs

+0

Ouch! Hack khủng khiếp! –

+0

Có một vài vấn đề với giải pháp này. Đối với một (và tôi nên đã được rõ ràng hơn trong bài gốc của tôi), tôi muốn có một giải pháp mà sẽ làm việc ngay cả khi nội dung của 'second.js' là không rõ. Và 2) nếu bạn phá vỡ mọi thứ mà các cuộc gọi 'second.js', bạn gần như chắc chắn sẽ gặp lỗi. Tôi không muốn lỗi. Thậm chí thay thế 'document.getElementById' bằng một hàm no-op sẽ gây ra vấn đề khi' second.js' cố gắng làm điều gì đó với phần tử kết quả. –

Các vấn đề liên quan