Passando i parametri ai file JavaScript


163

Spesso avrò un file JavaScript che voglio usare che richiede che determinate variabili siano definite nella mia pagina web.

Quindi il codice è qualcosa del genere:

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

Ma quello che voglio fare è:

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

Ho provato diversi metodi e il migliore ancora è quello di analizzare la stringa di query in questo modo:

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

E poi cerca i miei valori.

Mi chiedo se esiste un altro modo per farlo senza creare una funzione per analizzare la mia stringa.

Conoscete tutti altri metodi?


9
Questa è una domanda duplicata ( stackoverflow.com/questions/1017424/… ) e secondo me un progetto errato. È molto più semplice utilizzare la prima soluzione variabile (va bene) o fare in modo che lo script definisca una funzione che viene quindi chiamata con le variabili (meglio).
Matthew Flaschen,

Sto cercando una soluzione aggiornata che sfrutti la nuova funzionalità ECMAScript 6 ... se presente
Chef_Code

L'uso di qualsiasi script interno attiverà i report CSP se il sito è HTTPS e se CSP è completamente abilitato. Il motivo indicato nel documento CSP è che consentire elementi interni può facilitare l'iniezione.
David Spector,

Risposte:


207

Consiglierei di non usare le variabili globali, se possibile. Utilizzare uno spazio dei nomi e OOP per passare gli argomenti a un oggetto.

Questo codice appartiene 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 nel tuo file html:

<script type="text/javascript" src="file.js"></script>
<script type="text/javascript">
   MYLIBRARY.init(["somevalue", 1, "controlId"]);
   MYLIBRARY.helloWorld();
</script>

4
@Naeem - è file.js caricato il 100% delle volte in cui MYLIBRARY.init (["somevalue", 1, "controlId"]); viene eseguito? Forse abbiamo bisogno del documento pronto per metterlo?
Darius.V,

2
1) Se esiste MYLIBRARY, usalo o definisci un nuovo oggetto. L'intento è quello di creare uno spazio dei nomi globale, normalmente userò gli spazi dei nomi secondari in modo che questa linea aiuti a evitare di ridefinire lo spazio dei nomi di livello superiore. 2) Sì, ha creato un singleton.
Naeem Sarfraz,

1
Ho fatto un esempio di questa idea: http://plnkr.co/edit/iE0Vr7sszfqrrDIsR8Wi?p=preview
robe007

1
Cosa significa questa espressione? var MYLIBRARY = MYLIBRARY || (function(){}());? Perché dovrebbe MYLIBRARYessere impostato su un valore booleano?
Alex,

1
@Alexander, vedi il mio commento sopra stackoverflow.com/questions/2190801/…
Naeem Sarfraz,

79

È possibile passare parametri con attributi arbitrari. Funziona con tutti i browser recenti.

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

All'interno di somefile.js puoi ottenere i valori delle variabili passate in questo modo:

........

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

...eccetera...


1
Ottima soluzione, funziona anche con l'attributo asincrono.
ViRuSTriNiTy

2
vecchio ma per quelli che cercano su Google: è fantastico se devi aggirare un CSP (politica di sicurezza dei contenuti). Utilizziamo molti script in cui passiamo stringhe determinate da risorse linguistiche e chiavi API ecc. In file JS.
monkeySeeMonkeyDo

2
Nota che puoi anche usare document.currentScript, vedi caniuse.com/#feat=document-currentscript per la compatibilità (o usa un polyfill)
Yves M.

La $(..)sintassi è jquery, non dimenticare di includerla.
Timo

48

Un'altra idea mi sono imbattuto è stata l'assegnazione di un idal <script>elemento e passando gli argomenti come data-*attributi. Il <script>tag risultante sarebbe simile al seguente:

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

Lo script potrebbe quindi utilizzare l'id per localizzarsi a livello di codice e analizzare gli argomenti. Dato il <script>tag precedente , il nome potrebbe essere recuperato in questo modo:

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

Otteniamo name=helper


4
Se non vuoi dipendere dall'attributo id, puoi anche fare in modo che lo helper.jsscript esegua il ciclo di tutti i scripttag della pagina, cercando quello con scr="helper.js", quindi estrai data-name.
jvannistelrooy,

1
@jvannistelrooy Non ho provato ma credo che si dovrebbe essere in grado di arrivare alla sceneggiatura tramite jquery e un selettore intelligente come questo: var this_js_script = $('script[src*=somefile]');(copiato dall'alto )
LosManos,

19

Dai un'occhiata a questo URL. Funziona perfettamente per il requisito.

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

Grazie mille all'autore. Per un rapido riferimento ho incollato la logica principale di seguito:

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

1
Mi è piaciuta questa soluzione, ma l'ho modificata leggermente, per usare l'identificatore di frammento, in modo che non rompa le cache: var frag = myScript.src.replace (/ ^ [^ \ #] + \ #? /, '') ;
Mark Nottingham,

Oh, e ho scoperto che mettere JSON lì dentro funziona, purché tu lo urlodi.
Mark Nottingham,

@ user378221 - sei sicuro che otterrà sempre il file giusto? Voglio dire, questo presuppone che questo file sia l'ultimo. Cosa succede se vengono caricati più tag di script prima che questo venga eseguito? Non so se ciò sia possibile, ma presumo che il documento non aspetti il ​​completamento dell'esecuzione del codice per caricare altri tag?
Darius.V,

Meglio usare Usa decodeURI () o decodeURIComponent () invece di annullare l'escape poiché quella funzione è obsoleta.
Steve Childs,

@ Darius.V Funziona con script non asincroni. Preferisco usare l' opzione 1 con l'opzione 5 come fallback .
Luca,

14

Si utilizzano variabili globali :-D.

Come questo:

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

Il codice JavaScript in "file.js" può accedere obj1e obj2senza problemi.

EDIT Voglio solo aggiungere che se 'file.js' vuole verificare se obj1e obj2sono stati addirittura dichiarati, puoi usare la seguente funzione.

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

Spero che questo ti aiuti.


Ciò genererà un errore senza riferimenti in IE. Il corpo preferito di quella funzione è return typeof window[$Name] !== 'undefined';. Si noti che si passerà a quella funzione una stringa contenente il nome della variabile. È probabilmente più semplice controllare semplicemente se la variabile esiste nel codice chiamante (non può essere implementata come funzione) tramite if (typeof var !== 'undefined').
Anthony DiSanti,

2
Oh, non usare mai IE. Scusate.
NawaMan,

Sì, ma secondo la mia esperienza con JavaScript, utilizzerei le variabili globali solo per gli spazi dei nomi dei moduli ed esporrei tanto quanto è necessario (come il codice di inizializzazione, alcune proprietà), come mostrato qui . È troppo facile ottenere conflitti di denominazione in progetti complessi ... passare ore del tuo tempo e poi scoprire che hai già usato la variabile da qualche altra parte nel progetto.
Matt,

Puoi omettere il var.
Timo

12

Ecco una prova del concetto molto affrettata.

Sono sicuro che ci sono almeno 2 posti in cui possono esserci miglioramenti, e sono anche sicuro che questo non sopravviverebbe a lungo in natura. Qualsiasi feedback per renderlo più presentabile o utilizzabile è il benvenuto.

La chiave sta impostando un ID per l'elemento dello script. L'unico problema è che questo significa che puoi chiamare lo script solo una volta poiché cerca quell'ID per estrarre la stringa di query. Ciò potrebbe essere risolto se, invece, lo script scorre tutti gli elementi della query per vedere se qualcuno di essi punta ad esso e, in tal caso, utilizza l'ultima istanza di tale elemento dello script. Comunque, con il codice:

Script chiamato:

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 chiamato tramite la seguente pagina:

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

<h1 id="docTitle"></h1>
<p id="docText"></p>

quindi eval imposterà il valore come variabile globale ... piuttosto interessante.
Rodmjay,

1
eval è il male scappare
Barry Chapman

@barrychapman - oh amico, 2010. Sono sorpreso che non ci fossero altre 10 bandiere rosse.
Anthony,

9

potrebbe essere molto semplice

per esempio

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

È quindi possibile convertire la stringa di query in json come di seguito

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

e quindi può usare like

console.log(json.id);

6

Questo può essere fatto facilmente se si utilizza un framework Javascript come jQuery. Così,

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

Ora puoi usare questi parametri dividendoli come parametri variabili.

Lo stesso processo può essere eseguito senza alcun framework ma richiederà alcune righe di codice in più.


2
C'è anche un plug-in jQuery chiamato parseQuery che lo rende ancora più semplice: plugins.jquery.com/project/parseQuery
Jimmy Cuadra

@JimmyCuadra - sfortunatamente, il link che hai fornito è rotto. Ma ho trovato un approccio simile in jQuery stesso: jQuery.param ()
Matt

1

Bene, potresti avere il file javascript creato da uno qualsiasi dei linguaggi di scripting, iniettando le tue variabili nel file ad ogni richiesta. Dovresti dire al tuo server web di non inviare staticamente js-files (basterebbe usare mod_rewrite).

Tuttavia, tieni presente che perdi la memorizzazione nella cache di questi file js poiché vengono costantemente modificati.

Ciao.


1
Non riesco a capire perché questo sia stato sottoposto a downgrade. È una soluzione perfettamente valida che ricorre alla logica lato server. A mio avviso, l'OP non richiede che sia una soluzione completamente javascript.
Anoyz,

@aefxx - Hai ragione. All'interno del <script>blocco è possibile utilizzare qualcosa di simile al MyModule.Initialize( '<%: Model.SomeProperty%>', '<%: Model.SomeOtherProperty%>', ...);rendering sul server. Attenzione: <%:sfugge al valore, mentre <%=invia il valore senza caratteri di escape. MyModulesarebbe uno spazio dei nomi (cioè una variabile all'interno del file .js che è auto-invocata, che espone la funzione Initialize). Ho votato a favore della tua risposta, perché è un contributo utile.
Matt,

Nota: il concetto, come creare un modulo che contiene una proprietà esposta, è descritto più in dettaglio qui in questa pagina.
Matt,

1

Bella domanda e risposte creative, ma il mio consiglio è di rendere i tuoi metodi parametrizzati e che dovrebbero risolvere tutti i tuoi problemi senza alcun trucco.

se hai la funzione:

function A()
{
    var val = external_value_from_query_string_or_global_param;
}

puoi cambiarlo in:

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

Penso che questo sia l'approccio più naturale, non è necessario creare ulteriore documentazione sui "parametri dei file" e si riceve lo stesso. Ciò è particolarmente utile se consenti ad altri sviluppatori di utilizzare il tuo file js.


0

No, non puoi davvero farlo aggiungendo variabili alla porzione querystring dell'URL del file JS. Se sta scrivendo la parte di codice per analizzare la stringa che ti disturba, forse un altro modo sarebbe json codificare le tue variabili e metterle in qualcosa come l'attributo rel del tag? Non so quanto sia valido in termini di convalida HTML, se è qualcosa di cui sei molto preoccupato. Quindi devi solo trovare l'attributo rel dello script e quindi json_decode.

per esempio

<script type='text/javascript' src='file.js' rel='{"myvar":"somevalue","anothervar":"anothervalue"}'></script>

2
Puoi farlo con la falsa stringa di querystring. Non è una buona idea. Ma neanche questo. L'attributo rel ha lo scopo di specificare un tipo di relazione di collegamento, non stipare dati casuali in un tag di script.
Matthew Flaschen,

1
^^ concordato. Non riesco a vedere alcun problema nel lasciarlo così com'è, e usando l'approccio <script> var obj1 = "somevalue" </script>, è il modo migliore e credo - il modo giusto.
Dal Hundal,

0

Non è html valido (non credo) ma sembra funzionare se crei un attributo personalizzato per il tag script nella tua pagina web :

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

Quindi accedi all'attributo personalizzato nel javascript:

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

Non sono sicuro che sia migliore o peggiore dell'analisi della stringa di origine dello script.


0

Se hai bisogno di un modo che superi il controllo CSP (che proibisce la non sicurezza in linea), allora devi usare il metodo nonce per aggiungere un valore univoco sia allo script che alla direttiva CSP o scrivere i tuoi valori nell'html e rileggerli.

Metodo nonce per 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>`)
})

o scrivere valori nel metodo html. in questo caso usando Jquery:

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


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

0

Sebbene questa domanda sia stata posta qualche tempo fa, è ancora rilevante ad oggi. Questo non è un approccio banale usando i parametri del file di script, ma ho già avuto alcuni casi d'uso estremi che in questo modo erano più adatti.

Mi sono imbattuto in questo post per trovare una soluzione migliore di quella che ho scritto qualche tempo fa, con la speranza di trovare forse una funzionalità nativa o qualcosa di simile.
Condividerò la mia soluzione, fino a quando non verrà implementata una migliore. Funziona con la maggior parte dei browser moderni, forse anche con quelli meno recenti, non ci ha provato.

Tutte le soluzioni di cui sopra, si basano sul fatto che deve essere iniettato con tag SCRIPT predefinito e ben marcato e si basano completamente sull'implementazione HTML. Ma cosa succede se lo script viene iniettato in modo dinamico, o peggio ancora, e se si scrive una libreria, che verrà utilizzata in una varietà di siti Web?
In questi e in altri casi, tutte le risposte di cui sopra non sono sufficienti e diventano persino troppo complicate.

Innanzitutto, proviamo a capire cosa dobbiamo raggiungere qui. Tutto quello che dobbiamo fare è ottenere l'URL dello script stesso, da lì è un gioco da ragazzi.

In realtà c'è un bel trucco per ottenere l'URL dello script dallo script stesso. Una delle funzionalità della Errorclasse nativa è la possibilità di fornire una traccia dello stack della "posizione problematica", inclusa la traccia esatta del file all'ultima chiamata. Per raggiungere questo obiettivo, userò la proprietà dello stack dell'istanza Error, che una volta creata fornirà la traccia dello stack completa.

Ecco come funziona la magia:

// The pattern to split each row in the stack trace string
const STACK_TRACE_SPLIT_PATTERN = /(?:Error)?\n(?:\s*at\s+)?/;
// For browsers, like Chrome, IE, Edge and more.
const STACK_TRACE_ROW_PATTERN1 = /^.+?\s\((.+?):\d+:\d+\)$/;
// For browsers, like Firefox, Safari, some variants of Chrome and maybe other browsers.
const STACK_TRACE_ROW_PATTERN2 = /^(?:.*?@)?(.*?):\d+(?::\d+)?$/;

const getFileParams = () => {
    const stack = new Error().stack;
    const row = stack.split(STACK_TRACE_SPLIT_PATTERN, 2)[1];
    const [, url] = row.match(STACK_TRACE_ROW_PATTERN1) || row.match(STACK_TRACE_ROW_PATTERN2) || [];
    if (!url) {
        console.warn("Something went wrong. You should debug it and find out why.");
        return;
    }
    try {
        const urlObj = new URL(url);
        return urlObj.searchParams; // This feature doesn't exists in IE, in this case you should use urlObj.search and handle the query parsing by yourself.
    } catch (e) {
        console.warn(`The URL '${url}' is not valid.`);
    }
}

Ora, in ogni caso di chiamata di script, come nel caso OP:

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

Nello file.jsscript ora puoi fare:

const params = getFileParams();
console.log(params.get('obj2'));
// Prints: someothervalue

Questo funzionerà anche con RequireJS e altri script di file iniettati dinamicamente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.