Como posso fazer referência à tag de script que carregou o script atualmente em execução?

303

Como posso referenciar o elemento de script que carregou o javascript atualmente em execução?

Aqui está a situação. Eu tenho um script "mestre" sendo carregado no alto da página, logo abaixo da tag 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>

Há um script em "scripts.js" que precisa ser capaz de carregar sob demanda de outros scripts. O método normal não funciona muito bem para mim, porque preciso adicionar novos scripts sem fazer referência à tag HEAD, porque o elemento HEAD não concluiu a renderização:

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

O que eu quero fazer é fazer referência ao elemento de script que carregou o script atual, para que eu possa acrescentar minhas novas tags de script carregadas dinamicamente ao DOM depois dele.

<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>
Shog9
fonte
1
Uma palavra de aviso: modificar o DOM enquanto ele ainda está carregando causará um mundo de mágoa no IE6 e IE7 . Você ficará melhor executando esse código após o carregamento da página.
Triptych
6
Parece que está acima na caniuse agora: caniuse.com/#feat=document-currentscript
Tyler

Respostas:

653

Como obter o elemento de script atual:

1. Use document.currentScript

document.currentScriptretornará o <script>elemento cujo script está sendo processado no momento.

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

Benefícios

  • Simples e explícito. Confiável.
  • Não precisa modificar a tag de script
  • Funciona com scripts assíncronos ( defer& async)
  • Funciona com scripts inseridos dinamicamente

Problemas

  • Não funcionará em navegadores mais antigos e no IE.
  • Não funciona com módulos <script type="module">

2. Selecione o script por id

Ao atribuir ao script um atributo id, você poderá selecioná-lo facilmente por id de dentro do uso document.getElementById().

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

Benefícios

  • Simples e explícito. Confiável.
  • Quase universalmente suportado
  • Funciona com scripts assíncronos ( defer& async)
  • Funciona com scripts inseridos dinamicamente

Problemas

  • Requer a adição de um atributo personalizado à tag de script
  • id atributo pode causar um comportamento estranho para scripts em alguns navegadores para alguns casos extremos

3. Selecione o script usando um data-*atributo

data-*Atribuir um atributo ao script permitirá que você o selecione facilmente de dentro.

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

Isso tem poucos benefícios em relação à opção anterior.

Benefícios

  • Simples e explícito.
  • Funciona com scripts assíncronos ( defer& async)
  • Funciona com scripts inseridos dinamicamente

Problemas

  • Requer a adição de um atributo personalizado à tag de script
  • HTML5 e querySelector()não compatível em todos os navegadores
  • Menos suportado do que usar o idatributo
  • Vai dar a volta <script>com idcasos de ponta.
  • Pode ficar confuso se outro elemento tiver o mesmo atributo e valor de dados na página.

4. Selecione o script por src

Em vez de usar os atributos de dados, você pode usar o seletor para escolher o script pela origem:

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

No embed.js:

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

Benefícios

  • Confiável
  • Funciona com scripts assíncronos ( defer& async)
  • Funciona com scripts inseridos dinamicamente
  • Nenhum atributo ou ID personalizado é necessário

Problemas

  • Será que não funciona para scripts locais
  • Causará problemas em diferentes ambientes, como Desenvolvimento e Produção
  • Estático e frágil. Alterar a localização do arquivo de script exigirá a modificação do script
  • Menos suportado do que usar o idatributo
  • Causará problemas se você carregar o mesmo script duas vezes

5. Passe por todos os scripts para encontrar o que você deseja

Também podemos percorrer todos os elementos do script e verificar cada um individualmente para selecionar o que queremos:

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

Isso nos permite usar as duas técnicas anteriores em navegadores mais antigos que não suportam querySelector()bem os atributos. Por exemplo:

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

Isso herda os benefícios e os problemas de qualquer abordagem adotada, mas não depende disso, querySelector()pois funcionará em navegadores mais antigos.

6. Obtenha o último script executado

Como os scripts são executados sequencialmente, o último elemento do script muitas vezes será o script em execução no momento:

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

Benefícios

  • Simples.
  • Quase universalmente suportado
  • Nenhum atributo ou ID personalizado é necessário

Problemas

  • Será que não trabalhar com scripts assíncronos ( defer& async)
  • Será que não trabalhar com scripts inseridos dinamicamente
brice
fonte
4
Essa deve ser a resposta.
Royi Namir 15/05
1
Concorde com @RoyiNamir. Esta é a melhor resposta.
Hans
27
Obrigado pessoal, mas você sabe que eu respondi 4 anos após a resposta aceita, certo :) #
brice 25/05
5
"document.currentScript" não funciona para mim com scripts dinâmicos carregados, retorna nulo no mais recente chrome / firefox, "último script executado" funciona ok
xwild 12/02/16
1
não funciona quando scriptestá em um templateinserido em um DOM Sombra
Supersharp
86

Como os scripts são executados sequencialmente, a tag de script atualmente executada é sempre a última tag de script da página até então. Portanto, para obter a tag de script, você pode:

var scripts = document.getElementsByTagName( 'script' );
var thisScriptTag = scripts[ scripts.length - 1 ];
Mordida de café
fonte
3
Isso é simples e elegante. Há um exemplo disso na nova API de gráficos / visualizações do Google se você descompactar o javascript. Eles carregam dados JSON de dentro da tag de script, consulte: ajax.googleapis.com/ajax/static/modules/gviz/1.0/chart.js
Jason Thrasher
1
Essa é uma ótima idéia, e normalmente funciona para mim. Mas devo acrescentar que há momentos em que o encontrei retornando uma referência a um script diferente. Não sei por que - não foi possível rastrear isso. Consequentemente, eu geralmente uso um método diferente, por exemplo, codifico o nome do arquivo de script e procuro a tag de script com esse nome de arquivo.
18711 Ken Smith
53
Uma instância em que posso pensar onde isso pode retornar resultados incorretos é quando uma tag de script é adicionada ao DOM de forma assíncrona.
Mordida de café
9
Sim, isso pode ter resultados imprevisíveis; portanto, você pode tentar usar um seletor: $ ('script [src * = "/ mysource.js"]') ???
Jason Sebring
7
Não funciona quando você tem scripts carregados após o carregamento da página. Você provavelmente não receberá a tag certa.
ThemeZ 22/02
11

Provavelmente, a coisa mais fácil a fazer seria idatribuir um atributo à sua tag de script .

Greg
fonte
2
Embora você esteja certo, há muitos casos em que a pergunta do OP é válida, alguns seriam: 1) quando você está rastreando 2) quando você está trabalhando com o DOM de um cliente e ele não está disposto a mudar
nichochar
10

Os scripts são executados sequencialmente apenas se eles não tiverem um atributo "adiar" ou "assíncrono". Conhecer um dos possíveis atributos ID / SRC / TITLE da tag de script também pode funcionar nesses casos. Então, as sugestões de Greg e Justin estão corretas.

Já existe uma proposta para um document.currentScriptnas listas WHATWG.

EDIT : Firefox> 4 já implementa essa propriedade muito útil, mas não está disponível no IE11 pela última vez que verifiquei e disponível apenas no Chrome 29 e Safari 8.

Edição : ninguém mencionou a coleção "document.scripts", mas acredito que o seguinte pode ser uma boa alternativa entre navegadores para obter o script atualmente em execução:

var me = document.scripts[document.scripts.length -1];
Diego Perini
fonte
1
É document.scripts não Document.Script
Moritz
9

Aqui está um pouco de um polyfill que aproveita document.CurrentScriptse existe e volta a encontrar o script por ID.

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

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

Se você incluir isso na parte superior de cada marca de script, acredito que você poderá saber constantemente qual marca de script está sendo acionada e também poderá fazer referência à marca de script no contexto de um retorno de chamada assíncrono.

Não testado, deixe comentários para os outros, se você tentar.

Art Lawry
fonte
O idatributo é inválido em um scriptelemento. Que tipos de problemas essa abordagem poderia gerar?
nr
1
@ nr - Não, todos os elementos podem ter um idatributo. id,, classe slotsão definidos no nível DOM, não no nível HTML. Se você acessar os atributos globais em HTML e rolar a lista, encontrará "O padrão DOM define os requisitos do agente do usuário para os atributos de classe, ID e slot para qualquer elemento em qualquer espaço de nome". seguido por "Os atributos de classe, ID e slot podem ser especificados em todos os elementos HTML". A especificação do DOM cobre aqui .
TJ Crowder
6

Ele deve funcionar no carregamento da página e quando uma tag de script é adicionada com javascript (por exemplo, com ajax)

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

Siga estas etapas simples para obter referência ao bloco de scripts em execução atual:

  1. Coloque uma sequência única aleatória no bloco de scripts (deve ser único / diferente em cada bloco de script)
  2. Repita o resultado de document.getElementsByTagName ('script'), procurando a sequência exclusiva de cada conteúdo (obtida da propriedade innerText / textContent).

Exemplo (ABCDE345678 é o ID exclusivo) :

<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, para o seu caso, você pode simplesmente usar o método document.write () à moda antiga para incluir outro script. Como você mencionou que o DOM ainda não foi renderizado, é possível aproveitar o fato de o navegador sempre executar o script em sequência linear (exceto o adiado que será renderizado posteriormente), portanto o restante do documento ainda "não existe". Tudo o que você escrever em document.write () será colocado logo após o script do chamador.

Exemplo de página HTML original :

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

Conteúdo de script.js :

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

Após renderizada, a estrutura do DOM se tornará:

HEAD
  SCRIPT script.js
  SCRIPT inserted.js
  SCRIPT otherscript.js
BODY
Ferdinand Liu
fonte
Isso parece funcionar apenas para scripts embutidos, não para scripts externos. No último caso, todas as propriedades innerText, text e textContent estão vazias.
Jos de Jong
3

Uma abordagem para lidar com scripts assíncronos e adiados é alavancar o manipulador de onload - defina um manipulador de onload para todas as tags de script e o primeiro a ser executado deve ser o seu.

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);
});
Pete Blois
fonte
3

Para obter o script, que atualmente carregou o script, você pode usar

var thisScript = document.currentScript;

Você precisa manter uma referência no início do seu script, para poder ligar mais tarde

var url = thisScript.src
Peter Lakatos - appnomads
fonte
2

Considere este algoritmo. Quando o seu script for carregado (se houver vários scripts idênticos), consulte document.scripts, encontre o primeiro script com o atributo "src" correto, salve-o e marque-o como 'visitado' com um atributo de dados ou nome de classe exclusivo.

Quando o próximo script for carregado, verifique novamente document.scripts, passando sobre qualquer script já marcado como visitado. Tome a primeira instância não visitada desse script.

Isso pressupõe que scripts idênticos provavelmente serão executados na ordem em que são carregados, da cabeça para o corpo, de cima para baixo, de síncrono para assíncrono.

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

Isso examinará todo o script para o primeiro script correspondente que não está marcado com o atributo especial.

Em seguida, marque esse nó, se encontrado, com um atributo de dados para que as verificações subsequentes não o escolham. Isso é semelhante ao gráfico dos algoritmos BFS e DFS, nos quais os nós podem ser marcados como 'visitados' para impedir a revisão.

LSOJ
fonte
Bem-vindo ao Stack Overflow. Você gostaria de incluir algum código no algoritmo?
Gary99
Thar you go, @ Gary99
LSOJ
0

Eu tenho isso, que está funcionando no FF3, IE6 e 7. Os métodos nos scripts carregados sob demanda não estão disponíveis até o carregamento da página ser concluído, mas isso ainda é muito útil.

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

fonte
0

Se você puder assumir o nome do arquivo do script, poderá encontrá-lo. Até agora, só testei realmente a seguinte função no 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');
Apenas apaixonado
fonte
1
Muito desatualizado. Isso pode ser feito com o querySelector, um oneliner simples!
Fabian von Ellerts
-1

Eu descobri o código a seguir como o mais consistente, com desempenho e simples.

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);
Travis Tidwell
fonte