Làm cách nào tôi có thể tham chiếu thẻ tập lệnh đã tải tập lệnh hiện đang thực thi?


303

Làm cách nào tôi có thể tham chiếu phần tử script đã tải javascript hiện đang chạy?

Đây là tình hình. Tôi có một tập lệnh "chính" đang được tải cao trong trang, điều đầu tiên dưới thẻ CHÍNH.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<script type="text/javascript" src="scripts.js"></script>

Có một tập lệnh trong "scripts.js" cần có khả năng tải các tập lệnh khác theo yêu cầu. Phương thức bình thường không hoàn toàn phù hợp với tôi vì tôi cần thêm các tập lệnh mới mà không cần tham chiếu thẻ HEAD, vì phần tử HEAD chưa hoàn thành kết xuất:

document.getElementsByTagName('head')[0].appendChild(v);

Những gì tôi muốn làm là tham chiếu phần tử tập lệnh đã tải tập lệnh hiện tại để sau đó tôi có thể nối các thẻ tập lệnh được tải động mới của mình vào DOM sau nó.

<script type="text/javascript" src="scripts.js"></script>
loaded by scripts.js--><script type="text/javascript" src="new_script1.js"></script>
loaded by scripts.js --><script type="text/javascript" src="new_script2.js"></script>

1
Một lời cảnh báo: sửa đổi DOM trong khi nó vẫn đang tải sẽ khiến bạn bị tổn thương trong IE6 & IE7 . Bạn sẽ tốt hơn khi chạy mã đó sau khi tải trang.
Triptych

6
Có vẻ như bây giờ đã có trên caniuse: caniuse.com/#feat=document-currentscript
Tyler

Câu trả lời:


653

Cách lấy phần tử script hiện tại:

1. Sử dụng document.currentScript

document.currentScriptsẽ trả về <script>phần tử có tập lệnh hiện đang được xử lý.

<script>
var me = document.currentScript;
</script>

Những lợi ích

  • Đơn giản và rõ ràng. Đáng tin cậy.
  • Không cần sửa đổi thẻ script
  • Hoạt động với các tập lệnh không đồng bộ ( defer& async)
  • Hoạt động với các tập lệnh được chèn động

Các vấn đề

  • Sẽ không hoạt động trong các trình duyệt cũ hơn và IE.
  • Không hoạt động với các mô-đun <script type="module">

2. Chọn tập lệnh theo id

Cung cấp cho tập lệnh một thuộc tính id sẽ cho phép bạn dễ dàng chọn nó theo id từ bên trong bằng cách sử dụng document.getElementById().

<script id="myscript">
var me = document.getElementById('myscript');
</script>

Những lợi ích

  • Đơn giản và rõ ràng. Đáng tin cậy.
  • Hầu như được hỗ trợ toàn cầu
  • Hoạt động với các tập lệnh không đồng bộ ( defer& async)
  • Hoạt động với các tập lệnh được chèn động

Các vấn đề

  • Yêu cầu thêm thuộc tính tùy chỉnh vào thẻ script
  • id thuộc tính có thể gây ra hành vi kỳ lạ cho các tập lệnh trong một số trình duyệt cho một số trường hợp cạnh

3. Chọn tập lệnh bằng data-*thuộc tính

Cung cấp cho tập lệnh một data-*thuộc tính sẽ cho phép bạn dễ dàng chọn nó từ bên trong.

<script data-name="myscript">
var me = document.querySelector('script[data-name="myscript"]');
</script>

Điều này có một vài lợi ích so với tùy chọn trước đó.

Những lợi ích

  • Đơn giản và rõ ràng.
  • Hoạt động với các tập lệnh không đồng bộ ( defer& async)
  • Hoạt động với các tập lệnh được chèn động

Các vấn đề

  • Yêu cầu thêm thuộc tính tùy chỉnh vào thẻ script
  • HTML5 và querySelector()không tuân thủ trong tất cả các trình duyệt
  • Ít được hỗ trợ hơn so với sử dụng idthuộc tính
  • Sẽ nhận được xung quanh <script>với idcác trường hợp cạnh.
  • Có thể bị lẫn lộn nếu một yếu tố khác có cùng thuộc tính và giá trị dữ liệu trên trang.

4. Chọn tập lệnh theo src

Thay vì sử dụng các thuộc tính dữ liệu, bạn có thể sử dụng bộ chọn để chọn tập lệnh theo nguồn:

<script src="//example.com/embed.js"></script>

Trong embed.js:

var me = document.querySelector('script[src="//example.com/embed.js"]');

Những lợi ích

  • Đáng tin cậy
  • Hoạt động với các tập lệnh không đồng bộ ( defer& async)
  • Hoạt động với các tập lệnh được chèn động
  • Không cần thuộc tính hoặc id tùy chỉnh

Các vấn đề

  • Liệu không làm việc cho các kịch bản trong nước
  • Sẽ gây ra vấn đề trong các môi trường khác nhau, như Phát triển và Sản xuất
  • Tĩnh và dễ vỡ. Thay đổi vị trí của tệp tập lệnh sẽ yêu cầu sửa đổi tập lệnh
  • Ít được hỗ trợ hơn so với sử dụng idthuộc tính
  • Sẽ gây ra sự cố nếu bạn tải cùng một tập lệnh hai lần

5. Lặp lại tất cả các tập lệnh để tìm tập lệnh bạn muốn

Chúng tôi cũng có thể lặp qua từng phần tử tập lệnh và kiểm tra từng phần riêng lẻ để chọn phần tử chúng tôi muốn:

<script>
var me = null;
var scripts = document.getElementsByTagName("script")
for (var i = 0; i < scripts.length; ++i) {
    if( isMe(scripts[i])){
      me = scripts[i];
    }
}
</script>

Điều này cho phép chúng tôi sử dụng cả hai kỹ thuật trước đó trong các trình duyệt cũ không hỗ trợ querySelector()tốt với các thuộc tính. Ví dụ:

function isMe(scriptElem){
    return scriptElem.getAttribute('src') === "//example.com/embed.js";
}

Điều này thừa hưởng những lợi ích và vấn đề của bất kỳ cách tiếp cận nào được thực hiện, nhưng không dựa vào querySelector()vì vậy sẽ hoạt động trong các trình duyệt cũ hơn.

6. Nhận kịch bản thực hiện cuối cùng

Vì các tập lệnh được thực thi tuần tự, nên phần tử tập lệnh cuối cùng sẽ rất thường là tập lệnh hiện đang chạy:

<script>
var scripts = document.getElementsByTagName( 'script' );
var me = scripts[ scripts.length - 1 ];
</script>

Những lợi ích

  • Đơn giản.
  • Hầu như được hỗ trợ toàn cầu
  • Không cần thuộc tính hoặc id tùy chỉnh

Các vấn đề

  • Liệu không làm việc với kịch bản không đồng bộ ( defer& async)
  • Liệu không làm việc với các kịch bản tự động chèn

4
Đây nên là câu trả lời.
Royi Namir

1
Đồng ý với @RoyiNamir. Đây là câu trả lời tốt nhất.
Hans

27
Cảm ơn các bạn, nhưng bạn biết tôi đã trả lời 4 năm sau câu trả lời được chấp nhận, phải không :)
brice

5
"document.cienScript" không hoạt động với tôi với các tập lệnh được tải động, trả về null trong chrome / firefox mới nhất, "tập lệnh được thực thi cuối cùng" hoạt động tốt
xwild 12/2/2016

1
không hoạt động khi scriptđược templatechèn trong Shadow DOM
Supersharp

86

Vì các tập lệnh được thực thi tuần tự, thẻ tập lệnh hiện được thực thi luôn là thẻ tập lệnh cuối cùng trên trang cho đến lúc đó. Vì vậy, để có được thẻ script, bạn có thể làm:

var scripts = document.getElementsByTagName( 'script' );
var thisScriptTag = scripts[ scripts.length - 1 ];

3
Điều này là đơn giản và thanh lịch. Có một ví dụ về nó trong API Biểu đồ / Trực quan hóa mới của Google nếu bạn giải nén javascript. Họ tải dữ liệu JSON từ trong thẻ script, xem: ajax.googleapis.com/ajax/static/modules/gviz/1.0/chart.js
Jason Thrasher

1
Đây là một ý tưởng tuyệt vời, và nó thường làm việc cho tôi. Nhưng tôi nên nói thêm rằng có những lúc tôi thấy nó trả về một tham chiếu cho một kịch bản khác. Không chắc chắn tại sao - không thể theo dõi điều đó. Do đó, tôi thường sử dụng một phương thức khác, ví dụ: tôi mã hóa tên của tệp tập lệnh và tìm thẻ tập lệnh có tên tệp đó.
Ken Smith

53
Một ví dụ mà tôi có thể nghĩ về nơi điều này có thể trả về kết quả không chính xác là khi thẻ script được thêm vào DOM không đồng bộ.
Cà phê cắn

9
Có, điều này có thể có kết quả không thể đoán trước vì vậy bạn có thể thử sử dụng bộ chọn thay thế: $ ('script [src * = "/ mysource.js"]') ???
Jason Sebring

7
Nó không hoạt động khi bạn có tập lệnh được tải sau khi tải trang. Bạn có thể sẽ không nhận được đúng thẻ.
ThemeZ

11

Có lẽ điều dễ nhất để làm là cung cấp cho thẻ scrip của bạn một idthuộc tính.


2
Mặc dù bạn đúng, có rất nhiều trường hợp trong đó câu hỏi của OP là hợp lệ, một cặp sẽ là: 1) khi bạn đang bò 2) khi bạn đang làm việc với DOM của khách hàng và anh ta không muốn thay đổi
nichochar

10

Tập lệnh được thực thi tuần tự chỉ khi chúng không có thuộc tính "defer" hoặc "async". Biết một trong các thuộc tính ID / SRC / TITLE có thể có của thẻ script cũng có thể hoạt động trong những trường hợp đó. Vì vậy, cả đề xuất của Greg và Justin đều đúng.

Đã có một đề xuất cho một document.currentScriptdanh sách WHATWG.

EDIT : Firefox> 4 đã triển khai thuộc tính rất hữu ích này nhưng nó không có sẵn trong IE11 tôi đã kiểm tra lần cuối và chỉ có sẵn trong Chrome 29 và Safari 8.

EDIT : Không ai đề cập đến bộ sưu tập "document.scripts" nhưng tôi tin rằng sau đây có thể là một sự thay thế trình duyệt chéo tốt để có được tập lệnh hiện đang chạy:

var me = document.scripts[document.scripts.length -1];

1
Đó là document.scripts không document.script
Moritz

9

Đây là một chút của một polyfill tận dụng document.CurrentScriptnếu nó tồn tại và quay trở lại tìm tập lệnh bằng ID.

<script id="uniqueScriptId">
    (function () {
        var thisScript = document.CurrentScript || document.getElementByID('uniqueScriptId');

        // your code referencing thisScript here
    ());
</script>

Nếu bạn bao gồm thẻ này ở đầu mỗi thẻ tập lệnh, tôi tin rằng bạn sẽ luôn có thể biết thẻ tập lệnh nào đang được kích hoạt và bạn cũng có thể tham chiếu thẻ tập lệnh trong ngữ cảnh gọi lại không đồng bộ.

Chưa được kiểm tra, vì vậy hãy để lại phản hồi cho người khác nếu bạn thử nó.


Các idthuộc tính là không hợp lệ trong một scriptyếu tố mặc dù. Cách tiếp cận này có thể tạo ra những vấn đề gì?
nr

1
@nr - Không, tất cả các yếu tố có thể có một idthuộc tính. id, classslotđược xác định ở cấp DOM, không phải ở cấp HTML. Nếu bạn đi đến các thuộc tính toàn cầu trong HTML và cuộn qua danh sách, bạn sẽ thấy "tiêu chuẩn DOM xác định các yêu cầu tác nhân người dùng cho các thuộc tính lớp, id và vị trí cho bất kỳ thành phần nào trong bất kỳ không gian tên nào." theo sau là "Các thuộc tính lớp, id và vị trí có thể được chỉ định trên tất cả các thành phần HTML." Thông số DOM bao gồm nó ở đây .
TJ Crowder

6

Nó phải hoạt động khi tải trang và khi thẻ script được thêm bằng javascript (ví dụ: với ajax)

<script id="currentScript">
var $this = document.getElementById("currentScript");
$this.setAttribute("id","");
//...
</script>

3

Thực hiện theo các bước đơn giản sau để có được tham chiếu đến khối tập lệnh thực thi hiện tại:

  1. Đặt một số chuỗi duy nhất ngẫu nhiên trong khối tập lệnh (phải là duy nhất / khác nhau trong mỗi khối tập lệnh)
  2. Lặp lại kết quả của document.getElementsByTagName ('script'), tìm chuỗi duy nhất từ ​​mỗi nội dung của chúng (thu được từ thuộc tính InternalText / textContent).

Ví dụ (ABCDE345678 là ID duy nhất) :

<script type="text/javascript">
var A=document.getElementsByTagName('script'),i=count(A),thi$;
for(;i;thi$=A[--i])
  if((thi$.innerText||thi$.textContent).indexOf('ABCDE345678'))break;
// Now thi$ is refer to current script block
</script>

btw, đối với trường hợp của bạn, bạn chỉ cần sử dụng phương thức document.write () kiểu cũ để bao gồm một tập lệnh khác. Như bạn đã đề cập rằng DOM chưa được hiển thị, bạn có thể tận dụng thực tế là trình duyệt luôn thực thi tập lệnh theo trình tự tuyến tính (ngoại trừ phần bị hoãn sẽ được hiển thị sau), vì vậy phần còn lại của tài liệu của bạn vẫn "không tồn tại". Bất cứ điều gì bạn viết thông qua document.write () sẽ được đặt ngay sau tập lệnh của người gọi.

Ví dụ về trang HTML gốc :

<!doctype html>
<html><head>
<script src="script.js"></script>
<script src="otherscript.js"></script>
<body>anything</body></html>

Nội dung của script.js :

document.write('<script src="inserted.js"></script>');

Sau khi được kết xuất, cấu trúc DOM sẽ trở thành:

HEAD
  SCRIPT script.js
  SCRIPT inserted.js
  SCRIPT otherscript.js
BODY

Điều này dường như chỉ hoạt động cho các tập lệnh nội tuyến, không phải cho các tập lệnh bên ngoài. Trong trường hợp sau, tất cả các thuộc tính bên trong, văn bản và textContent đều trống.
Jos de Jong

3

Cách tiếp cận để xử lý các tập lệnh không đồng bộ và trả chậm là tận dụng trình xử lý onload - thiết lập trình xử lý onload cho tất cả các thẻ script và tập lệnh đầu tiên thực thi phải là của bạn.

function getCurrentScript(callback) {
  if (document.currentScript) {
    callback(document.currentScript);
    return;
  }
  var scripts = document.scripts;
  function onLoad() {
    for (var i = 0; i < scripts.length; ++i) {
      scripts[i].removeEventListener('load', onLoad, false);
    }
    callback(event.target);
  }
  for (var i = 0; i < scripts.length; ++i) {
    scripts[i].addEventListener('load', onLoad, false);
  }
}

getCurrentScript(function(currentScript) {
  window.console.log(currentScript.src);
});

3

Để lấy tập lệnh, hiện đang tải tập lệnh bạn có thể sử dụng

var thisScript = document.currentScript;

Bạn cần giữ một tài liệu tham khảo ở đầu tập lệnh của mình, để bạn có thể gọi sau

var url = thisScript.src

2

Hãy xem xét thuật toán này. Khi tập lệnh của bạn tải (nếu có nhiều tập lệnh giống hệt nhau), hãy xem qua document.scripts, tìm tập lệnh đầu tiên có thuộc tính "src" chính xác và lưu tập lệnh đó và đánh dấu là 'đã truy cập' bằng thuộc tính dữ liệu hoặc tên lớp duy nhất.

Khi tập lệnh tiếp theo tải, quét qua document.scripts một lần nữa, chuyển qua bất kỳ tập lệnh nào đã được đánh dấu là đã truy cập. Lấy ví dụ đầu tiên của tập lệnh đó.

Điều này giả định rằng các tập lệnh giống hệt nhau sẽ có khả năng thực thi theo thứ tự mà chúng được tải, từ đầu đến thân, từ trên xuống dưới, từ đồng bộ đến không đồng bộ.

(function () {
  var scripts = document.scripts;

  // Scan for this data-* attribute
  var dataAttr = 'data-your-attribute-here';

  var i = 0;
  var script;
  while (i < scripts.length) {
    script = scripts[i];
    if (/your_script_here\.js/i.test(script.src)
        && !script.hasAttribute(dataAttr)) {

        // A good match will break the loop before
        // script is set to null.
        break;
    }

    // If we exit the loop through a while condition failure,
    // a check for null will reveal there are no matches.
    script = null;
    ++i;
  }

  /**
   * This specific your_script_here.js script tag.
   * @type {Element|Node}
   */
  var yourScriptVariable = null;

  // Mark the script an pass it on.
  if (script) {
    script.setAttribute(dataAttr, '');
    yourScriptVariable = script;
  }
})();

Điều này sẽ quét qua tất cả các tập lệnh cho tập lệnh phù hợp đầu tiên không được đánh dấu bằng thuộc tính đặc biệt.

Sau đó, đánh dấu nút đó, nếu được tìm thấy, với thuộc tính dữ liệu để các lần quét tiếp theo sẽ không chọn nó. Điều này tương tự như các thuật toán BFS và DFS truyền tải đồ thị trong đó các nút có thể được đánh dấu là 'đã truy cập' để ngăn việc xem lại.


Chào mừng bạn đến với Stack Overflow. Bạn có quan tâm để bao gồm một số mã với thuật toán?
Gary99

Thar bạn đi, @ Gary99
LSOJ

0

Tôi đã có cái này, đang hoạt động trong FF3, IE6 & 7. Các phương thức trong các tập lệnh được tải theo yêu cầu không có sẵn cho đến khi tải trang hoàn tất, nhưng điều này vẫn rất hữu ích.

//handle on-demand loading of javascripts
makescript = function(url){
    var v = document.createElement('script');
    v.src=url;
    v.type='text/javascript';

    //insertAfter. Get last <script> tag in DOM
    d=document.getElementsByTagName('script')[(document.getElementsByTagName('script').length-1)];
    d.parentNode.insertBefore( v, d.nextSibling );
}

0

Nếu bạn có thể giả sử tên tập tin của tập lệnh, bạn có thể tìm thấy nó. Cho đến nay tôi chỉ thực sự thử nghiệm chức năng sau trong Firefox.

  function findMe(tag, attr, file) {
    var tags = document.getElementsByTagName(tag);
    var r = new RegExp(file + '$');
    for (var i = 0;i < tags.length;i++) {
      if (r.exec(tags[i][attr])) {
        return tags[i][attr];
      }
    }
  };
  var element = findMe('script', 'src', 'scripts.js');

1
Rất lỗi thời. Điều này có thể được thực hiện với querySelector, một oneliner đơn giản!
Fabian von Ellerts

-1

Tôi đã tìm thấy đoạn mã sau đây là nhất quán, hiệu suất và đơn giản nhất.

var scripts = document.getElementsByTagName('script');
var thisScript = null;
var i = scripts.length;
while (i--) {
  if (scripts[i].src && (scripts[i].src.indexOf('yourscript.js') !== -1)) {
    thisScript = scripts[i];
    break;
  }
}
console.log(thisScript);
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.