Как я могу сослаться на тег script, который загрузил выполняемый в данный момент скрипт? - PullRequest
257 голосов
/ 31 декабря 2008

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

Ответы [ 13 ]

571 голосов
/ 30 марта 2014

Как получить текущий элемент скрипта:

1. Используйте document.currentScript

document.currentScript вернет элемент <script>, сценарий которого в данный момент обрабатывается.

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

Преимущества

  • Просто и явно. Надежный.
  • Не нужно изменять тег сценария
  • Работает с асинхронными сценариями (defer & async)
  • Работает со скриптами, вставляемыми динамически

Проблемы

  • Не будет работать в старых браузерах и IE.

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>

Преимущества

  • Simple.
  • Почти повсеместно поддерживается
  • Не требуется пользовательских атрибутов или идентификатора

Проблемы

  • Работает ли * с асинхронными сценариями (defer & async)
  • Работает ли не со сценариями, вставляемыми динамически
87 голосов
/ 24 июля 2010

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

var scripts = document.getElementsByTagName( 'script' );
var thisScriptTag = scripts[ scripts.length - 1 ];
11 голосов
/ 31 декабря 2008

Вероятно, проще всего было бы присвоить тегу script атрибут id.

10 голосов
/ 12 сентября 2010

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

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

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

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

var me = document.scripts[document.scripts.length -1];
9 голосов
/ 13 сентября 2013

Вот немного полифилла, который использует document.CurrentScript, если он существует и возвращается к поиску скрипта по идентификатору.

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

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

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

Не проверено, поэтому оставьте отзыв для других, если вы попробуете.

6 голосов
/ 18 октября 2011

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

<script id="currentScript">
var $this = document.getElementById("currentScript");
$this.setAttribute("id","");
//...
</script>
3 голосов
/ 25 февраля 2018

Чтобы получить скрипт, который сейчас загружен, вы можете использовать

var thisScript = document.currentScript;

Вам нужно сохранить ссылку в начале вашего скрипта, чтобы вы могли позвонить позже

var url = thisScript.src
3 голосов
/ 07 августа 2013

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

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);
});
2 голосов
/ 12 июня 2017

Рассмотрим этот алгоритм. Когда ваш скрипт загружается (если есть несколько идентичных скриптов), просмотрите 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, где узлы могут быть помечены как «посещенные» для предотвращения повторного посещения.

2 голосов
/ 26 февраля 2012

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

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