Passando parâmetros para arquivos JavaScript

163

Frequentemente, terei um arquivo JavaScript que desejo usar, que requer que determinadas variáveis ​​sejam definidas na minha página da web.

Portanto, o código é algo como isto:

<script type="text/javascript" src="file.js"></script>
<script type="text/javascript">
   var obj1 = "somevalue";
</script>

Mas o que eu quero fazer é:

<script type="text/javascript" 
     src="file.js?obj1=somevalue&obj2=someothervalue"></script>

Eu tentei métodos diferentes e o melhor ainda é analisar a string de consulta assim:

var scriptSrc = document.getElementById("myscript").src.toLowerCase();

E, em seguida, pesquise meus valores.

Gostaria de saber se existe outra maneira de fazer isso sem criar uma função para analisar minha string.

Vocês todos conhecem outros métodos?

RaduM
fonte
9
Esta é uma pergunta duplicada ( stackoverflow.com/questions/1017424/… ) e, na minha opinião, um design equivocado. É muito mais simples usar sua primeira solução de variável (ok) ou fazer com que o script defina uma função que é chamada com as variáveis ​​(melhor).
Matthew Flaschen
Eu estou procurando uma solução atualizada que tira proveito da nova funcionalidade ECMAScript 6 ... se houver
Chef_Code
O uso de qualquer script interno acionará relatórios de CSP se o site for HTTPS e se o CSP estiver totalmente ativado. O motivo apresentado no documento CSP é que permitir elementos internos pode facilitar a injeção.
David Spector

Respostas:

207

Eu recomendo não usar variáveis ​​globais, se possível. Use um espaço para nome e OOP para passar seus argumentos para um objeto.

Este código pertence a file.js:

var MYLIBRARY = MYLIBRARY || (function(){
    var _args = {}; // private

    return {
        init : function(Args) {
            _args = Args;
            // some other initialising
        },
        helloWorld : function() {
            alert('Hello World! -' + _args[0]);
        }
    };
}());

E no seu arquivo html:

<script type="text/javascript" src="file.js"></script>
<script type="text/javascript">
   MYLIBRARY.init(["somevalue", 1, "controlId"]);
   MYLIBRARY.helloWorld();
</script>
Naeem Sarfraz
fonte
4
@Naeem - é file.js carregado 100% do tempo quando MYLIBRARY.init (["somevalue", 1, "controlId"]); é executado? Talvez precisemos de um documento pronto para colocá-lo?
Devius.V
2
1) Se MYLIBRARY existir, use-o ou defina um novo objeto. A intenção é criar um espaço para nome global, normalmente usarei sub-espaços para nome, para que essa linha ajude a evitar a redefinição do espaço para nome de nível superior. 2) Sim, ele criou um singleton.
Naeem Sarfraz
1
Fiz um exemplo dessa ideia: http://plnkr.co/edit/iE0Vr7sszfqrrDIsR8Wi?p=preview
robe007
1
O que essa expressão significa? var MYLIBRARY = MYLIBRARY || (function(){}());? Por que deve MYLIBRARYser definido como um valor booleano?
21716 Alex
1
@Alexander, veja meu comentário acima stackoverflow.com/questions/2190801/…
Naeem Sarfraz
79

Você pode passar parâmetros com atributos arbitrários. Isso funciona em todos os navegadores recentes.

<script type="text/javascript" data-my_var_1="some_val_1" data-my_var_2="some_val_2" src="/js/somefile.js"></script>

Dentro do somefile.js, você pode obter valores de variáveis ​​passados ​​da seguinte maneira:

........

var this_js_script = $('script[src*=somefile]'); // or better regexp to get the file name..

var my_var_1 = this_js_script.attr('data-my_var_1');   
if (typeof my_var_1 === "undefined" ) {
   var my_var_1 = 'some_default_value';
}
alert(my_var_1); // to view the variable value

var my_var_2 = this_js_script.attr('data-my_var_2');   
if (typeof my_var_2 === "undefined" ) {
   var my_var_2 = 'some_default_value';
}
alert(my_var_2); // to view the variable value

... etc ...

Vlado
fonte
1
Solução muito boa, isso funciona mesmo com o atributo async.
ViRuSTriNiY
2
antigo, mas para aqueles que pesquisam no Google: isso é ótimo se você precisar contornar um CSP (política de segurança de conteúdo). Usamos muitos scripts nos quais passamos cadeias determinadas a partir de recursos de idioma e chaves de API etc. para arquivos JS.
precisa saber é o seguinte
2
Observe que você também pode usar document.currentScript, consulte caniuse.com/#feat=document-currentscript para compatibilidade (ou use um polyfill)
Yves M.
A $(..)sintaxe é jquery, não esqueça de incluí-la.
Timo
48

Outra ideia que me ocorreu foi atribuir um idao <script>elemento e passar os argumentos como data-*atributos. A <script>tag resultante ficaria assim:

<script id="helper" data-name="helper" src="helper.js"></script>

O script pode então usar o id para localizar-se programaticamente e analisar os argumentos. Dada a <script>tag anterior , o nome pode ser recuperado assim:

var name = document.getElementById("helper").getAttribute("data-name");

Temos name=helper

Nalini Wanjale
fonte
4
Se você não quiser depender do atributo id, também poderá fazer com que seu helper.jsscript faça um loop por todas as scripttags da página, procurando pela que contém scr="helper.js"e depois extraia data-name.
jvannistelrooy
1
@jvannistelrooy Eu não tentei, mas eu acredito que deve ser capaz de obter para o script através de jquery e um seletor inteligente assim: var this_js_script = $('script[src*=somefile]');(copiado de acima )
LosManos
19

Confira este URL. Está funcionando perfeitamente para a exigência.

http://feather.elektrum.org/book/src.html

Muito obrigado ao autor. Para uma referência rápida, colei a lógica principal abaixo:

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

var queryString = myScript.src.replace(/^[^\?]+\??/,'');

var params = parseQuery( queryString );

function parseQuery ( query ) {
  var Params = new Object ();
  if ( ! query ) return Params; // return empty object
  var Pairs = query.split(/[;&]/);
  for ( var i = 0; i < Pairs.length; i++ ) {
    var KeyVal = Pairs[i].split('=');
    if ( ! KeyVal || KeyVal.length != 2 ) continue;
    var key = unescape( KeyVal[0] );
    var val = unescape( KeyVal[1] );
    val = val.replace(/\+/g, ' ');
    Params[key] = val;
  }
  return Params;
}
user378221
fonte
1
Eu gostei dessa solução, mas a modifiquei um pouco, para usar o identificador de fragmento, para que não atrapalhe os caches: var frag = myScript.src.replace (/ ^ [^ \ #] + \ #? /, '') ;
Mark Nottingham
Ah, e eu descobri que colocar JSON lá funciona, desde que você o codifique.
Mark Nottingham
@ user378221 - você tem certeza de que sempre obterá o arquivo certo? Quero dizer, isso pressupõe que este arquivo seja o último. E se mais tags de script forem carregadas antes da execução? Não sei se isso é possível, mas presumo que o documento não aguarde a execução do código para carregar outras tags?
Devius.V
Melhor usar Use decodeURI () ou decodeURIComponent () em vez de desescapar, pois essa função está obsoleta.
Steve Childs
@ Darius.V Isso funciona para scripts não assíncronos. Prefiro usar a opção 1 com a opção 5 como substituto .
Luke
14

Você usa variáveis ​​globais :-D.

Como isso:

<script type="text/javascript">
   var obj1 = "somevalue";
   var obj2 = "someothervalue";
</script>
<script type="text/javascript" src="file.js"></script">

O código JavaScript em 'file.js' pode acessar obj1e obj2sem problemas.

EDIT Apenas deseja adicionar que se 'file.js' quiser verificar se obj1e obj2mesmo foi declarado, você pode usar a seguinte função.

function IsDefined($Name) {
    return (window[$Name] != undefined);
}

Espero que isto ajude.

NawaMan
fonte
Isso gerará um erro não referenciado no IE. O corpo preferido dessa função é return typeof window[$Name] !== 'undefined';. Observe que você passaria para essa função uma string contendo o nome da variável. É provavelmente mais fácil simplesmente verificar se a variável existe no código de chamada (isso não pode ser implementado como uma função) via if (typeof var !== 'undefined').
Anthony DiSanti 9/09/11
2
Oh, nunca use o IE. Desculpe.
NawaMan 15/10/12
Sim, mas de acordo com minha experiência com JavaScript, eu usaria variáveis ​​globais apenas para namespaces de módulo e expunha apenas o necessário (como código init, algumas propriedades), como mostrado aqui . É muito fácil obter conflitos de nomenclatura em projetos complexos ... gastando horas do seu tempo e depois descobrindo que você já usou a variável em outro lugar do projeto.
Matt
Você pode omitir o var.
Timo
12

Aqui está uma prova de conceito muito apressada.

Tenho certeza de que há pelo menos 2 lugares onde pode haver melhorias, e também tenho certeza de que isso não sobreviveria por muito tempo na natureza. Qualquer comentário para torná-lo mais apresentável ou utilizável é bem-vindo.

A chave é definir um ID para o seu elemento de script. O único problema é que isso significa que você pode chamar o script apenas uma vez, pois ele procura esse ID para puxar a cadeia de consulta. Isso pode ser corrigido se, em vez disso, o script percorrer todos os elementos de consulta para ver se algum deles aponta para ele e, se houver, usar a última instância desse elemento de script. Enfim, continue com o código:

Script sendo chamado:

window.onload = function() {
//Notice that both possible parameters are pre-defined.
//Which is probably not required if using proper object notation
//in query string, or if variable-variables are possible in js.
var header;
var text;

//script gets the src attribute based on ID of page's script element:
var requestURL = document.getElementById("myScript").getAttribute("src");

//next use substring() to get querystring part of src
var queryString = requestURL.substring(requestURL.indexOf("?") + 1, requestURL.length);

//Next split the querystring into array
var params = queryString.split("&");

//Next loop through params
for(var i = 0; i < params.length; i++){
 var name  = params[i].substring(0,params[i].indexOf("="));
 var value = params[i].substring(params[i].indexOf("=") + 1, params[i].length);

    //Test if value is a number. If not, wrap value with quotes:
    if(isNaN(parseInt(value))) {
  params[i] = params[i].replace(value, "'" + value + "'");
 }

    // Finally, use eval to set values of pre-defined variables:
 eval(params[i]);
}

//Output to test that it worked:
document.getElementById("docTitle").innerHTML = header;
document.getElementById("docText").innerHTML = text;
};

Script chamado através da seguinte página:

<script id="myScript" type="text/javascript" 
        src="test.js?header=Test Page&text=This Works"></script>

<h1 id="docTitle"></h1>
<p id="docText"></p>
Anthony
fonte
então eval realmente definirá o valor como variável global ... bem legal.
Rodmjay
1
eval é o mal fugir
Barry Chapman
@ barrychapman - oh cara, 2010. Estou surpreso por não haver outras 10 bandeiras vermelhas lá.
1113 Anthony
9

pode ser muito simples

por exemplo

<script src="js/myscript.js?id=123"></script>
<script>
    var queryString = $("script[src*='js/myscript.js']").attr('src').split('?')[1];
</script>

Você pode converter a string de consulta em json como abaixo

var json = $.parseJSON('{"' 
            + queryString.replace(/&/g, '","').replace(/=/g, '":"') 
            + '"}');

e então pode usar como

console.log(json.id);
Tofeeq
fonte
6

Isso pode ser feito facilmente se você estiver usando alguma estrutura Javascript como o jQuery. Igual a,

var x = $('script:first').attr('src'); //Fetch the source in the first script tag
var params = x.split('?')[1]; //Get the params

Agora você pode usar esses parâmetros dividindo como seus parâmetros variáveis.

O mesmo processo pode ser feito sem nenhuma estrutura, mas será necessário mais algumas linhas de código.

GeekTantra
fonte
2
Há também um plugin jQuery chamado parseQuery que torna isso ainda mais fácil: plugins.jquery.com/project/parseQuery
Jimmy Cuadra
@JimmyCuadra - infelizmente, o link que você forneceu está quebrado. Mas eu achei uma abordagem semelhante em si jQuery: jQuery.param ()
Matt
1

Bem, você pode ter o arquivo javascript sendo construído por qualquer uma das linguagens de script, injetando suas variáveis ​​no arquivo a cada solicitação. Você teria que dizer ao seu servidor da web para não distribuir os arquivos js estaticamente (usar mod_rewrite seria suficiente).

Esteja ciente de que você perde qualquer cache desses arquivos js, pois eles são alterados constantemente.

Tchau.

aefxx
fonte
1
Não consigo entender por que isso foi rebaixado. É uma solução perfeitamente válida, recorrendo à lógica do lado do servidor. Tanto quanto vejo, o OP não exige que seja uma solução totalmente em javascript.
Anoyz
@aefxx - Você está certo. Dentro do <script>bloco, você pode usar algo parecido com o MyModule.Initialize( '<%: Model.SomeProperty%>', '<%: Model.SomeOtherProperty%>', ...);que é renderizado no servidor. Cuidado: <%:escapa o valor, enquanto <%=envia o valor sem escape. MyModuleseria um espaço para nome (ou seja, uma variável dentro do arquivo .js que é chamada automaticamente, o que expõe a função Initialize). Votei na sua resposta, porque é uma contribuição útil.
Matt
Nota: O conceito de como criar um módulo que contém uma propriedade exposta é descrito em mais detalhes aqui nesta página.
Matt
1

Boa pergunta e respostas criativas, mas minha sugestão é tornar seus métodos parametrizados e isso deve resolver todos os seus problemas sem truques.

se você tem função:

function A()
{
    var val = external_value_from_query_string_or_global_param;
}

você pode mudar isso para:

function B(function_param)
{
    var val = function_param;
}

Eu acho que essa é a abordagem mais natural, você não precisa criar documentação extra sobre 'parâmetros de arquivo' e recebe o mesmo. Isso é especialmente útil se você permitir que outros desenvolvedores usem seu arquivo js.

dzida
fonte
0

Não, você realmente não pode fazer isso adicionando variáveis ​​à parte da string de consulta da URL do arquivo JS. Se estiver escrevendo a parte do código para analisar a string que o incomoda, talvez outra maneira seria codificar json suas variáveis ​​e colocá-las em algo como o atributo rel da tag? Não sei como isso é válido em termos de validação HTML, se é algo com que você está muito preocupado. Então você só precisa encontrar o atributo rel do script e, em seguida, json_decode.

por exemplo

<script type='text/javascript' src='file.js' rel='{"myvar":"somevalue","anothervar":"anothervalue"}'></script>
Dal Hundal
fonte
2
Você pode fazer isso com a string de consulta falsa. Não é apenas uma boa ideia. Mas também não é isso. O atributo rel destina-se a especificar um tipo de relacionamento de link, não empilhar dados aleatórios em uma tag de script.
Matthew Flaschen
1
^^ concordou. Não vejo nenhum problema em deixá-lo como está e usando a abordagem <script> var obj1 = "somevalue" </script>, é a melhor maneira e acredito - da maneira certa.
Dal Hundal 03/02/10
0

Não é um html válido (acho que não), mas parece funcionar se você criar um atributo personalizado para a tag de script em sua página da web :

<script id="myScript" myCustomAttribute="some value" ....>

Em seguida, acesse o atributo personalizado no javascript:

var myVar = document.getElementById( "myScript" ).getAttribute( "myCustomAttribute" );

Não tenho certeza se isso é melhor ou pior do que analisar a cadeia de origem do script.

Cor
fonte
0

Se você precisar de uma maneira que seja aprovada na verificação do CSP (que proíbe a entrada não segura), use o método nonce para adicionar um valor exclusivo ao script e à diretiva CSP ou escreva seus valores no html e leia-os novamente.

Método Nonce para express.js:

const uuidv4 = require('uuid/v4')

app.use(function (req, res, next) {
  res.locals.nonce = uuidv4()
  next()
})

app.use(csp({
  directives: {
    scriptSrc: [
      "'self'",
      (req, res) => `'nonce-${res.locals.nonce}'`  // 'nonce-614d9122-d5b0-4760-aecf-3a5d17cf0ac9'
    ]
  }
}))

app.use(function (req, res) {
  res.end(`<script nonce="${res.locals.nonce}">alert(1 + 1);</script>`)
})

ou escreva valores no método html. neste caso, usando Jquery:

<div id="account" data-email="{{user.email}}"></div>
...


$(document).ready(() => {
    globalThis.EMAIL = $('#account').data('email');
}
David Dehghan
fonte