Як я можу посилатися на тег скрипта, який завантажив сценарій, що виконується в даний момент?


303

Як я можу посилатись на елемент сценарію, який завантажив JavaScript, який зараз працює?

Ось ситуація. У мене "головний" скрипт завантажується високо на сторінці, перш за все під тегом HEAD.

<!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>

У "scriptpts.js" є сценарій, який повинен мати можливість завантажувати інші сценарії на вимогу. Звичайний метод не дуже працює для мене, тому що мені потрібно додавати нові сценарії без посилання на тег HEAD, оскільки елемент HEAD не закінчив надання:

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

Що я хочу зробити, це посилання на елемент сценарію, який завантажив поточний скрипт, щоб я міг після цього додавати свої нові динамічно завантажені теги скрипту в DOM.

<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
Слово попередження: зміна DOM, поки він все ще завантажується, заподіює вам світ у IE6 та IE7 . Вам буде краще запустити цей код після завантаження сторінки.
Триптих

6
Виглядає, що зараз на каніусі: caniuse.com/#feat=document-currentscript
Тайлер

Відповіді:


653

Як отримати поточний елемент сценарію:

1. Використовуйте document.currentScript

document.currentScriptповерне <script>елемент, сценарій якого зараз обробляється.

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

Переваги

  • Просте і явне. Надійний.
  • Не потрібно змінювати тег сценарію
  • Працює з асинхронними сценаріями ( defer& async)
  • Працює зі сценаріями, вставленими динамічно

Проблеми

  • Не працюватимуть у старих браузерах та IE.
  • Не працює з модулями <script type="module">

2. Виберіть сценарій за ідентифікатором

Надання сценарію атрибута id дозволить вам легко вибрати його за ідентифікатором зсередини використання document.getElementById().

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

Переваги

  • Просте і явне. Надійний.
  • Майже повсюдно підтримується
  • Працює з асинхронними сценаріями ( defer& async)
  • Працює зі сценаріями, вставленими динамічно

Проблеми

  • Потрібно додати спеціальний атрибут до тегу сценарію
  • id атрибут може спричинити дивну поведінку сценаріїв у деяких браузерах для деяких кращих випадків

3. Виберіть сценарій за допомогою data-*атрибута

Надання data-*атрибуту сценарію дозволить вам легко вибрати його зсередини.

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

Це має мало переваг порівняно з попереднім варіантом.

Переваги

  • Просте і явне.
  • Працює з асинхронними сценаріями ( defer& async)
  • Працює зі сценаріями, вставленими динамічно

Проблеми

  • Потрібно додати спеціальний атрибут до тегу сценарію
  • HTML5 і querySelector()не сумісний у всіх браузерах
  • Менш широко підтримується, ніж використання idатрибута
  • Оберемося <script>з idкрайовими корпусами.
  • Може заплутатися, якщо інший елемент має такий самий атрибут даних та значення на сторінці.

4. Виберіть сценарій src

Замість використання атрибутів даних ви можете використовувати селектор для вибору сценарію за джерелом:

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

У embed.js:

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

Переваги

  • Надійний
  • Працює з асинхронними сценаріями ( defer& async)
  • Працює зі сценаріями, вставленими динамічно
  • Не потрібні спеціальні атрибути чи ідентифікатор

Проблеми

  • Хто НЕ працює для локальних скриптів
  • Викличе проблеми в різних середовищах, таких як Розвиток та Виробництво
  • Статичний і крихкий. Зміна місця розташування файлу сценарію потребує зміни сценарію
  • Менш широко підтримується, ніж використання idатрибута
  • Виникнуть проблеми, якщо ви завантажите один і той же сценарій двічі

5. Проведіть петлю над усіма сценаріями, щоб знайти потрібний

Ми також можемо перевести цикл на кожен елемент сценарію та перевірити кожен окремо, щоб вибрати потрібний:

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

Це дозволяє нам використовувати обидві попередні методи в старих браузерах, які не підтримують querySelector()атрибутів. Наприклад:

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

Це успадковує переваги та проблеми будь-якого підходу, але не покладається на querySelector()це, працюватиме у старих браузерах.

6. Отримайте останній виконаний сценарій

Оскільки сценарії виконуються послідовно, останнім елементом сценарію дуже часто буде поточний сценарій:

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

Переваги

  • Простий.
  • Майже повсюдно підтримується
  • Не потрібні спеціальні атрибути чи ідентифікатор

Проблеми

  • Хто НЕ працює з асинхронними скриптами ( defer& async)
  • Чи має НЕ працювати зі сценаріями , вставлених динамічно

4
Це має бути відповіддю.
Рой Намір

1
Погодьтеся з @RoyiNamir. Це найкраща відповідь.
Ганс

27
Дякую, хлопці, але ви знаєте, що я відповів через 4 роки після прийнятої відповіді, правда :)
привіт

5
"document.currentScript" не працює для мене з динамічно завантаженими сценаріями, повертає null в останніх chrome / firefox, "останній виконаний сценарій" працює нормально
xwild

1
не працює, коли scriptвін templateвставлений у Shadow DOM
Supersharp

86

Оскільки сценарії виконуються послідовно, тег сценарію, який виконується в даний час, завжди є останнім тегом сценарію на сторінці до цього часу. Отже, щоб отримати тег сценарію, ви можете:

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

3
Це просто і елегантно. Приклад цього є в новому API Google Charts / Visualizations, якщо ви розпакуйте javascript. Вони завантажують дані JSON у тег скрипту, див.: Ajax.googleapis.com/ajax/static/modules/gviz/1.0/chart.js
Jason Thrasher

1
Це відмінна ідея, і для мене це нормально працює. Але слід додати, що бувають випадки, коли я виявляю, що він повертає посилання на інший сценарій. Не впевнений, чому - не зміг це відстежити. Отже, я зазвичай переходжу з іншим методом, наприклад, я жорстко кодую ім'я файлу сценарію та шукаю тег сценарію з цим ім'ям файлу.
Кен Сміт

53
Один екземпляр, який я можу придумати, де це може повернути неправильні результати, - це коли асинхронно доданий тег сценарію до DOM.
кавовий укус

9
Так, це може мати непередбачувані результати, тож ви можете спробувати скористатися селектором: $ ('script [src * = "/ mysource.js"]') ???
Джейсон Себрінг

7
Він не працює, якщо у вас завантажені сценарії після завантаження сторінки. Ви, ймовірно, не отримаєте потрібного тегу.
ТемаZ

11

Напевно, найпростіше зробити це надати idатрибуту тегу сценарію.


2
Хоча ви маєте рацію, є багато випадків, коли питання ОП є дійсним, пара буде: 1) коли ви повзаєте 2) коли ви працюєте з DOM клієнта, і він не бажає змінюватися
nichochar

10

Сценарій виконується послідовно, лише якщо вони не мають атрибута "відкладати" або "асинхронізувати". Знання одного з можливих атрибутів ID / SRC / TITLE тегу сценарію може працювати і в цих випадках. Тож і пропозиції Грега та Джастіна є правильними.

У document.currentScriptсписках WHATWG вже є пропозиція .

EDIT : Firefox> 4 вже реалізує цю дуже корисну властивість, але вона недоступна в IE11 востаннє, яку я перевірив, і доступна лише в Chrome 29 та Safari 8.

EDIT : Ніхто не згадав про колекцію "document.scripts", але я вважаю, що наступне може бути хорошою альтернативою для перегляду в браузері для отримання поточного сценарію:

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

1
Це document.scripts not document.script
Моріц

9

Ось трохи полізаливу, який використовує, document.CurrentScriptякщо він існує, і повертається до пошуку сценарію за ідентифікатором.

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

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

Якщо ви включите це у верхній частині кожного тегу сценарію, я вважаю, що ви зможете послідовно знати, який тег сценарію відтворюється, і ви також зможете посилатися на тег сценарію в контексті асинхронного зворотного виклику.

Не перевірено, тому залиште відгуки для інших, якщо ви спробуєте.


idАтрибут недійсний в scriptелементі , хоча. Які проблеми можуть спричинити цей підхід?
Н.Р.

1
@nr - Ні, всі елементи можуть мати idатрибут. id,, classі slotвизначаються на рівні DOM, а не на рівні HTML. Якщо ви перейдете до глобальних атрибутів у HTML та прокрутите повз списку, ви знайдете "Стандарт DOM визначає вимоги до атрибутів користувача для атрибутів класу, id та слота для будь-якого елемента будь-якого простору імен". з наступним "Атрибути класу, id та слота можуть бути вказані для всіх елементів HTML." Специфікація DOM висвітлює його тут .
TJ Crowder

6

Він повинен працювати при завантаженні сторінки та коли тег сценарію додається з javascript (наприклад, з ajax)

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

3

Виконайте наступні прості дії, щоб отримати посилання на поточний блок сценарію виконання:

  1. Помістіть якусь випадкову унікальну рядок всередині блоку сценарію (він повинен бути унікальним / різним у кожному блоці сценарію)
  2. Ітераційний результат document.getElementsByTagName ('скрипт'), шукаючи унікальний рядок з кожного їх вмісту (отриманий із властивості innerText / textContent).

Приклад (ABCDE345678 - це унікальний ідентифікатор) :

<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, для вашого випадку ви можете просто використовувати старомодний метод document.write () для включення іншого сценарію. Як ви вже згадували, що DOM ще не наданий, ви можете скористатися тим, що браузер завжди виконує скрипт у лінійній послідовності (за винятком відкладеного, який буде надано пізніше), тому решта вашого документа все ще "не існує". Все, що ви пишете через document.write (), буде розміщено відразу після скрипту виклику.

Приклад оригінальної сторінки HTML :

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

Вміст script.js :

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

Після надання, структура DOM стане:

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

Здається, це працює лише для вбудованих сценаріїв, а не для зовнішніх сценаріїв. В останньому випадку всі властивості InternalText, text та textContent порожні.
Джос де Йонг

3

Підхід для роботи з асинхронними та відкладеними сценаріями полягає в тому, щоб використовувати обробник завантаження - встановити обробник завантаження для всіх тегів сценарію, і перший, який виконується, повинен бути вашим.

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

Щоб отримати скрипт, завантажений на даний момент скрипт, який ви можете використовувати

var thisScript = document.currentScript;

Вам потрібно зберегти посилання на початку сценарію, щоб потім зателефонувати

var url = thisScript.src

2

Розглянемо цей алгоритм. Коли ваш скрипт завантажується (якщо є кілька однакових сценаріїв), перегляньте document.scripts, знайдіть перший скрипт з правильним атрибутом "src" та збережіть його та позначте як "відвідане" атрибутом data або унікальним className.

Коли наступний скрипт завантажиться, перегляньте документи Documentsscripts знову, передаючи будь-який сценарій, який уже позначений як відвіданий. Візьміть перший невидимий екземпляр цього сценарію.

Це передбачає, що ідентичні сценарії, ймовірно, виконуватимуться в тому порядку, в якому вони завантажуються, від голови до тіла, зверху вниз, від синхронного до асинхронного.

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

Це дозволить просканувати весь сценарій для першого відповідного сценарію, який не позначений спеціальним атрибутом.

Потім позначте цей вузол, якщо його знайдете, атрибутом даних, щоб наступні сканування не вибрали його. Це схоже на алгоритми BFS та DFS проходження графіків, де вузли можуть бути позначені як "відвідані", щоб запобігти повторному відвідуванню.


Ласкаво просимо до переповнення стека. Чи хотіли б ви включити якийсь код до алгоритму?
Gary99

Thar you go, @ Gary99
LSOJ

0

У мене це є, що працює в FF3, IE6 та 7. Методи в скриптах, завантажених на вимогу, недоступні до завершення завантаження сторінки, але це все ще дуже корисно.

//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

Якщо ви можете припустити ім'я файлу сценарію, ви можете його знайти. Я лише справді протестував наступну функцію у 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
Дуже застаріла. Це можна зробити за допомогою querySelector, простого oneliner!
Фабіан фон Еллертс

-1

Я знайшов наступний код найбільш послідовним, найефективнішим та простим.

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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.