Как я могу сослаться на тег script, который загрузил выполняемый в данный момент скрипт?


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>

В "scripts.js" есть скрипт, который должен уметь загружать другие скрипты по требованию. Обычный метод не совсем подходит для меня, потому что мне нужно добавить новые скрипты без ссылки на тег HEAD, потому что элемент HEAD не закончил рендеринг:

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

Я хочу сослаться на элемент script, который загрузил текущий скрипт, чтобы затем я мог добавить свои новые динамически загруженные теги script в 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: 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

Придание скрипту атрибута id позволит вам легко выбрать его по 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» не работает для меня с динамически загруженными скриптами, возвращает ноль в последнем chrome / firefox, «последний выполненный скрипт» работает нормально
xwild

1
не работает , когда scriptнаходится в templateвставленном в Shadow DOM
сверхкороткий

86

Поскольку сценарии выполняются последовательно, выполняемый в данный момент тег сценария всегда является последним тегом сценария на странице до тех пор. Итак, чтобы получить тег script, вы можете сделать:

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

3
Это просто и элегантно. Пример этого есть в новом Google Charts / Visualizations API, если вы распаковываете javascript. Они загружают данные JSON из тега скрипта, см. Ajax.googleapis.com/ajax/static/modules/gviz/1.0/chart.js
Джейсон Трашер

1
Это отличная идея, и она обычно работает для меня. Но я должен добавить, что бывают случаи, когда я возвращаю ссылку на другой скрипт. Не уверен почему - не смог отследить это. Следовательно, я обычно использую другой метод, например, я жестко кодирую имя файла сценария и ищу тег сценария с этим именем файла.
Кен Смит

53
Один пример, который я могу придумать, где это может привести к неверным результатам, - это когда тег сценария добавляется в DOM асинхронно.
Кофе Bite

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

7
Это не работает, когда у вас есть скрипты, загруженные после загрузки страницы. Вы, вероятно, не получите правильный тег.
ThemeZ

11

Вероятно, самое простое, что нужно сделать, - это присвоить тегу script idатрибут.


2
Хотя вы правы, есть много случаев, когда вопрос OP является действительным, пара будет: 1) когда вы сканируете 2) когда вы работаете с DOM клиента, и он не хочет менять
ничочар

10

Скрипты выполняются последовательно, только если они не имеют атрибута «defer» или «async». Знание одного из возможных атрибутов ID / SRC / TITLE тега script может работать и в этих случаях. Так что предложения Грега и Джастина верны.

В document.currentScriptсписках WHATWG уже есть предложение .

РЕДАКТИРОВАТЬ : Firefox> 4 уже реализует это очень полезное свойство, но оно недоступно в IE11 в последний раз, когда я проверял, и доступно только в Chrome 29 и Safari 8.

РЕДАКТИРОВАТЬ : Никто не упомянул коллекцию «document.scripts», но я считаю, что следующее может быть хорошей кросс-браузерной альтернативой для получения текущего запущенного скрипта:

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

1
Это document.scripts, а не 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 определяет требования к пользовательскому агенту для атрибутов class, id и slot для любого элемента в любом пространстве имен». затем «Атрибуты class, id и slot могут быть указаны для всех элементов HTML». Спецификация DOM покрывает это здесь .
TJ Crowder

6

Он должен работать при загрузке страницы и при добавлении тега сценария с javascript (например, с ajax)

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

3

Выполните следующие простые шаги, чтобы получить ссылку на текущий исполняемый блок скрипта:

  1. Поместите некоторую случайную уникальную строку в блок скрипта (должен быть уникальным / отличаться в каждом блоке скрипта)
  2. Повторяйте результат document.getElementsByTagName ('script'), просматривая уникальную строку из каждого их содержимого (полученного из свойства 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>

Кстати, для вашего случая вы можете просто использовать старомодный метод 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

Кажется, это работает только для встроенных сценариев, но не для внешних сценариев. В последнем случае все свойства innerText, text и textContent пусты.
Джос де Йонг

3

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

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.

Когда следующий скрипт загрузится, снова просмотрите файл document.scripts, пропустив любой скрипт, уже помеченный как посещенный. Возьмите первый невидимый экземпляр этого скрипта.

Это предполагает, что идентичные сценарии, вероятно, будут выполняться в том порядке, в котором они загружены, с головы до тела, сверху вниз, с синхронного на асинхронный.

(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

Чем ты идешь, @ 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.