Pasar parámetros a archivos JavaScript

163

A menudo tendré un archivo JavaScript que quiero usar que requiere que se definan ciertas variables en mi página web.

Entonces el código es algo como esto:

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

Pero lo que quiero hacer es:

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

Intenté diferentes métodos y el mejor todavía es analizar la cadena de consulta de esta manera:

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

Y luego busca mis valores.

Me pregunto si hay otra forma de hacer esto sin construir una función para analizar mi cadena.

¿Conocen todos otros métodos?

RaduM
fuente
9
Esta es una pregunta duplicada ( stackoverflow.com/questions/1017424/… ) y, en mi opinión, un diseño equivocado. Es mucho más simple usar su primera solución variable (está bien) o hacer que el script defina una función que luego se llama con las variables (mejor).
Matthew Flaschen
Estoy buscando una solución actualizada que aproveche la nueva funcionalidad ECMAScript 6 ... si hay alguna
Chef_Code
El uso de cualquier script interno activará los informes CSP si el sitio es HTTPS y si CSP está totalmente habilitado. La razón dada en el documento CSP es que permitir elementos internos puede facilitar la inyección.
David Spector

Respuestas:

207

Recomiendo no usar variables globales si es posible. Use un espacio de nombres y OOP para pasar sus argumentos a un objeto.

Este código pertenece en 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]);
        }
    };
}());

Y en tu archivo html:

<script type="text/javascript" src="file.js"></script>
<script type="text/javascript">
   MYLIBRARY.init(["somevalue", 1, "controlId"]);
   MYLIBRARY.helloWorld();
</script>
Naeem Sarfraz
fuente
44
@Naeem: se carga file.js el 100% del tiempo cuando MYLIBRARY.init (["somevalue", 1, "controlId"]); se ejecuta? Tal vez necesitamos en el documento listo para ponerlo?
Darius.V
2
1) Si existe MYLIBRARY, úselo o defina un nuevo objeto. La intención es crear un espacio de nombres global, normalmente usaré espacios de nombres secundarios para que esta línea ayude a evitar redefinir el espacio de nombres de nivel superior. 2) Sí, ha creado un singleton.
Naeem Sarfraz
1
He hecho un ejemplo de esta idea: http://plnkr.co/edit/iE0Vr7sszfqrrDIsR8Wi?p=preview
robe007
1
Que significa esta expresión? var MYLIBRARY = MYLIBRARY || (function(){}());? ¿Por qué debería MYLIBRARYestablecerse en un valor booleano?
Alex
1
@Alexander, mira mi comentario arriba stackoverflow.com/questions/2190801/…
Naeem Sarfraz
79

Puede pasar parámetros con atributos arbitrarios. Esto funciona en todos los navegadores recientes.

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

Dentro de somefile.js puede obtener valores de variables pasados ​​de esta manera:

........

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
fuente
1
Muy buena solución, esto incluso funciona con el atributo asíncrono.
ViRuSTriNiTy
2
antiguo, pero para aquellos que buscan en Google: esto es genial si necesita trabajar alrededor de un CSP (política de seguridad de contenido). Usamos muchos scripts en los que pasamos cadenas determinadas a partir de recursos de idioma y claves API, etc. a archivos JS.
monkeySeeMonkeyDo
2
Tenga en cuenta que también puede usar document.currentScript, ver caniuse.com/#feat=document-currentscript para compatibilidad (o usar un polyfill)
Yves M.
La $(..)sintaxis es jquery, no olvides incluirla.
Timo
48

Otra idea que encontré fue asignar un elemento idal <script>elemento y pasar los argumentos comodata-* atributos. La <script>etiqueta resultante se vería así:

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

El script podría usar la identificación para ubicarse programáticamente y analizar los argumentos. Dado lo anterior<script> etiqueta , el nombre podría recuperarse así:

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

Obtenemos name=helper

Nalini Wanjale
fuente
44
Si no desea depender del atributo id, también puede hacer que su helper.jsscript recorra todas las scriptetiquetas de la página, busque la que tiene scr="helper.js"y luego extraiga data-name.
jvannistelrooy
1
@jvannistelrooy No lo he intentado, pero creo que uno debería poder acceder al script a través de jquery y un selector inteligente como este: var this_js_script = $('script[src*=somefile]');(copiado desde arriba )
LosManos
19

Mira esta URL. Está funcionando perfectamente para el requisito.

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

Muchas gracias al autor. Para una referencia rápida, pegué la lógica principal a continuación:

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;
}
usuario378221
fuente
1
Me gustó esta solución, pero la modifiqué un poco, para usar el identificador de fragmento, de modo que no reviente cachés: var frag = myScript.src.replace (/ ^ [^ \ #] + \ #? /, '') ;
Mark Nottingham
Ah, y descubrí que poner JSON allí funciona, siempre que lo codifiques.
Mark Nottingham el
@ user378221: ¿está seguro de que siempre obtendrá el archivo correcto? Quiero decir que esto supone que este archivo es el último. ¿Qué sucede si se cargan más etiquetas de script antes de que esto se ejecute? No sé si eso es posible, pero supongo que el documento no espera a que finalice la ejecución del código para cargar otras etiquetas.
Darius.V
Mejor usar Use decodeURI () o decodeURIComponent () en lugar de no escapar ya que esa función está en desuso.
Steve Childs
@ Darius.V Esto funciona para scripts no asíncronos. Prefiero usar la opción 1 con la opción 5 como alternativa .
Lucas
14

Utiliza variables globales :-D.

Me gusta esto:

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

El código JavaScript en 'file.js' puede acceder obj1yobj2 sin problemas.

EDITAR Solo quiero agregar que si 'file.js' quiere verificar si obj1e obj2incluso ha sido declarado, puede usar la siguiente función.

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

Espero que esto ayude.

NawaMan
fuente
Eso arrojará un error sin referencia en IE. El cuerpo preferido de esa función es return typeof window[$Name] !== 'undefined';. Tenga en cuenta que pasaría esa función una cadena que contiene el nombre de la variable. Es probable que sea más fácil simplemente verificar si la variable existe en el código de llamada (esto no se puede implementar como una función) a través de if (typeof var !== 'undefined').
Anthony DiSanti
2
Oh, nunca uses IE. Lo siento.
NawaMan
Sí, pero según mi experiencia con JavaScript, usaría variables globales solo para espacios de nombres de módulos y expondría tanto como lo que se necesita (como código de inicio, algunas propiedades), como se muestra aquí . Es demasiado fácil obtener conflictos de nombres en proyectos complejos ... pasar horas de su tiempo y luego descubrir que ya ha utilizado la variable en otro lugar del proyecto.
Matt
Puedes omitir el var.
Timo
12

Aquí hay una prueba de concepto muy apresurada.

Estoy seguro de que hay al menos 2 lugares donde puede haber mejoras, y también estoy seguro de que esto no sobreviviría mucho en la naturaleza. Cualquier comentario para hacerlo más presentable o utilizable es bienvenido.

La clave es establecer una identificación para su elemento de script. El único inconveniente es que esto significa que solo puede llamar al script una vez, ya que busca esa ID para extraer la cadena de consulta. Esto podría solucionarse si, en cambio, el script recorre todos los elementos de consulta para ver si alguno de ellos apunta a él, y si es así, utiliza la última instancia de dicho elemento de script. De todos modos, adelante con el código:

Guión que se llama:

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 llamado a través de la siguiente 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>
Antonio
fuente
entonces eval realmente establecerá el valor como variable global ... bastante bueno.
rodmjay
1
eval is evil run away
Barry Chapman
@barrychapman - oh hombre, 2010. Me sorprende que no haya otras 10 banderas rojas allí.
Anthony
9

podría ser muy simple

por ejemplo

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

Luego puede convertir la cadena de consulta en json como a continuación

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

y luego puede usar como

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

Esto se puede hacer fácilmente si está utilizando un marco Javascript como jQuery. Al igual que,

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

Ahora puede usar estos parámetros dividiéndolos como sus parámetros variables.

El mismo proceso se puede hacer sin ningún marco, pero tomará más líneas de código.

GeekTantra
fuente
2
También hay un complemento jQuery llamado parseQuery que lo hace aún más fácil: plugins.jquery.com/project/parseQuery
Jimmy Cuadra
@JimmyCuadra: lamentablemente, el enlace que proporcionó está roto. Pero encontré un enfoque similar en jQuery: jQuery.param ()
Matt
1

Bueno, podría tener el archivo javascript creado por cualquiera de los lenguajes de script, inyectando sus variables en el archivo en cada solicitud. Tendría que decirle a su servidor web que no distribuya archivos js estáticamente (sería suficiente usar mod_rewrite).

Sin embargo, tenga en cuenta que pierde el almacenamiento en caché de estos archivos js ya que se modifican constantemente.

Adiós.

aefxx
fuente
1
No puedo entender por qué esto fue rechazado. Es una solución perfectamente válida que recurre a la lógica del lado del servidor. Por lo que veo, el OP no requiere que sea una solución totalmente javascript.
Anoyz
@aefxx: tienes razón. Dentro del <script>bloque puede usar algo como lo MyModule.Initialize( '<%: Model.SomeProperty%>', '<%: Model.SomeOtherProperty%>', ...);que se representa en el servidor. Precaución: <%:escapa al valor, mientras <%=envía el valor sin escape. MyModulesería un espacio de nombres (es decir, una variable dentro del archivo .js que se autoinvoca, que expone la función Initialize). Voté su respuesta, porque es una contribución útil.
Matt
Nota: El concepto de cómo crear un Módulo que contiene una propiedad expuesta se describe con más detalle aquí en esta página.
Matt
1

Buena pregunta y respuestas creativas, pero mi sugerencia es hacer que sus métodos estén paramterizados y eso debería resolver todos sus problemas sin ningún truco.

si tienes función:

function A()
{
    var val = external_value_from_query_string_or_global_param;
}

puedes cambiar esto a:

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

Creo que este es el enfoque más natural, no necesita crear documentación adicional sobre 'parámetros de archivo' y recibe lo mismo. Esto es especialmente útil si permite que otros desarrolladores utilicen su archivo js.

dzida
fuente
0

No, realmente no puede hacer esto agregando variables a la porción de cadena de consulta de la URL del archivo JS. Si está escribiendo la parte del código para analizar la cadena que te molesta, ¿quizás otra forma sería codificar json tus variables y ponerlas en algo como el atributo rel de la etiqueta? No sé qué tan válido es esto en términos de validación de HTML, si eso es algo que te preocupa mucho. Luego solo necesita encontrar el atributo rel del script y luego json_decode eso.

p.ej

<script type='text/javascript' src='file.js' rel='{"myvar":"somevalue","anothervar":"anothervalue"}'></script>
Dal Hundal
fuente
2
Puede hacerlo con la cadena de consulta falsa. Simplemente no es una buena idea. Pero tampoco es esto. El atributo rel está destinado a especificar un tipo de relación de enlace, no a agrupar datos aleatorios en una etiqueta de script.
Matthew Flaschen
1
^^ de acuerdo. No puedo ver ningún problema simplemente dejándolo como está, y usar el enfoque <script> var obj1 = "somevalue" </script>, es la mejor manera, y creo que es la manera correcta.
Dal Hundal
0

No es html válido (no lo creo) pero parece funcionar si crea un atributo personalizado para la etiqueta de script en su página web :

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

Luego acceda al atributo personalizado en javascript:

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

No estoy seguro de si esto es mejor o peor que analizar la cadena de origen del script.

Cor
fuente
0

Si necesita una forma que pase la verificación CSP (que prohíbe la inseguridad en línea), entonces debe usar un método nonce para agregar un valor único tanto al script como a la directiva CSP o escribir sus valores en el html y leerlos nuevamente.

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>`)
})

o escribir valores al método html. en este caso usando Jquery:

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


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