Przekazywanie parametrów do plików JavaScript


163

Często będę mieć plik JavaScript, którego chcę użyć, który wymaga zdefiniowania pewnych zmiennych na mojej stronie internetowej.

Więc kod wygląda mniej więcej tak:

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

Ale chcę zrobić:

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

Wypróbowałem różne metody, a najlepszą z nich jest przeanalizowanie ciągu zapytania w następujący sposób:

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

A potem poszukaj moich wartości.

Zastanawiam się, czy istnieje inny sposób na zrobienie tego bez budowania funkcji analizującej mój ciąg.

Czy wszyscy znacie inne metody?


9
To jest zduplikowane pytanie ( stackoverflow.com/questions/1017424/… ) i moim zdaniem błędny projekt. O wiele łatwiej jest albo użyć pierwszego rozwiązania dla zmiennych (dobrze), albo pozwolić skryptowi zdefiniować funkcję, która jest następnie wywoływana ze zmiennymi (lepiej).
Matthew Flaschen

Szukam zaktualizowanego rozwiązania, które wykorzystuje nową funkcjonalność ECMAScript 6 ... jeśli istnieje
Chef_Code

Użycie dowolnego skryptu wewnętrznego spowoduje wygenerowanie raportów CSP, jeśli witryna korzysta z protokołu HTTPS i jeśli CSP jest w pełni włączony. Powód podany w dokumencie CSP jest taki, że dopuszczenie elementów wewnętrznych może ułatwić wstrzykiwanie.
David Spector

Odpowiedzi:


207

Jeśli to możliwe, nie zalecałbym używania zmiennych globalnych. Użyj przestrzeni nazw i OOP, aby przekazać swoje argumenty do obiektu.

Ten kod należy do pliku.js:

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

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

A w Twoim pliku html:

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

4
@Naeem - plik.js jest ładowany przez 100% czasu, gdy MYLIBRARY.init (["somevalue", 1, "controlId"]); zostanie stracony? Może potrzebujemy gotowego dokumentu do umieszczenia go?
Darius.V

2
1) Jeśli istnieje MOJA BIBLIOTEKA, użyj jej lub zdefiniuj nowy obiekt. Celem jest utworzenie globalnej przestrzeni nazw, zwykle będę używał podprzestrzeni nazw, więc ta linia pomoże uniknąć ponownego definiowania przestrzeni nazw najwyższego poziomu. 2) Tak, stworzył singleton.
Naeem Sarfraz

1
Zrobiłem przykład tego pomysłu: http://plnkr.co/edit/iE0Vr7sszfqrrDIsR8Wi?p=preview
robe007

1
Co oznacza to wyrażenie? var MYLIBRARY = MYLIBRARY || (function(){}());? Dlaczego należy MYLIBRARYustawić wartość logiczną?
Alex

1
@Alexander, zobacz mój komentarz powyżej stackoverflow.com/questions/2190801/…
Naeem Sarfraz

79

Możesz przekazywać parametry z dowolnymi atrybutami. Działa to we wszystkich najnowszych przeglądarkach.

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

Wewnątrz somefile.js możesz pobrać wartości zmiennych w następujący sposób:

........

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

...itp...


1
Bardzo fajne rozwiązanie, działa to nawet z atrybutem async.
ViRuSTriNiTy

2
stare, ale dla tych, którzy googlują: jest to świetne, jeśli musisz obejść CSP (politykę bezpieczeństwa treści). Używamy wielu skryptów, w których przekazujemy ciągi znaków określone z zasobów językowych i kluczy API itp. Do plików JS.
monkeySeeMonkeyDo

2
Pamiętaj, że możesz również użyć document.currentScript, zobacz caniuse.com/#feat=document-currentscript w celu uzyskania zgodności (lub użyj polyfill)
Yves M.

$(..)Składni jQuery, nie zapomnij podać go.
Timo

48

Innym pomysłem natknąłem został przypisania iddo <script>elementu i przechodzącej argumenty jako data-*atrybutów. Wynikowy <script>tag wyglądałby mniej więcej tak:

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

Skrypt mógłby następnie użyć identyfikatora do programistycznego zlokalizowania siebie i przeanalizowania argumentów. Biorąc pod uwagę poprzedni <script>tag, nazwę można pobrać w następujący sposób:

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

Otrzymujemy name=helper


4
Jeśli nie chcesz polegać na atrybucie id, możesz również wykonać helper.jspętlę skryptu przez wszystkie scripttagi na stronie, odszukać ten z scr="helper.js", a następnie wyodrębnić data-name.
jvannistelrooy

1
@jvannistelrooy Nie próbowałem, ale uważam, że powinien być w stanie dostać się do skryptu za pomocą jquery i sprytnego selektora, takiego jak ten: var this_js_script = $('script[src*=somefile]');(skopiowane z góry )
LosManos

19

Sprawdź ten adres URL. Działa idealnie na wymagania.

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

Wielkie dzięki dla autora. Dla szybkiego odniesienia wkleiłem poniżej główną logikę:

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
Podobało mi się to rozwiązanie, ale nieznacznie je zmodyfikowałem, aby użyć identyfikatora fragmentu, aby nie zepsuło pamięci podręcznych: var frag = myScript.src.replace (/ ^ [^ \ #] + \ #? /, '') ;
Mark Nottingham,

Aha, i odkryłem, że umieszczanie JSON w tym miejscu działa, o ile kodujesz go urlen.
Mark Nottingham,

@ user378221 - czy jesteś pewien, że to zawsze dostanie właściwy plik? Mam na myśli to przy założeniu, że ten plik jest ostatni. Co się stanie, jeśli więcej tagów skryptu zostanie załadowanych przed wykonaniem tego? Nie wiem, czy jest to możliwe, ale zakładam, że dokument nie czeka na zakończenie wykonywania kodu, aby załadować inne tagi?
Darius.V

Lepiej użyć Użyj decodeURI () lub decodeURIComponent () zamiast unescape, ponieważ ta funkcja jest przestarzała.
Steve Childs

@ Darius.V Działa to w przypadku skryptów innych niż asynchroniczne. Preferuję opcję 1 z opcją 5 jako rezerwę .
Łukasz

14

Używasz zmiennych globalnych :-D.

Lubię to:

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

Kod JavaScript w „file.js” może uzyskać dostęp obj1i obj2bez problemu.

EDIT tylko dodać, że jeśli „” file.js chce sprawdzić, czy obj1i obj2były nawet zadeklarował można użyć następujących funkcji.

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

Mam nadzieję że to pomoże.


Spowoduje to wyświetlenie błędu bez odwołań w IE. Preferowaną treścią tej funkcji jest return typeof window[$Name] !== 'undefined';. Zwróć uwagę, że przekazałbyś tej funkcji ciąg znaków zawierający nazwę zmiennej. Prawdopodobnie łatwiej jest po prostu sprawdzić, czy zmienna istnieje w kodzie wywołującym (nie można tego zaimplementować jako funkcja) za pomocą if (typeof var !== 'undefined').
Anthony DiSanti,

2
Och, nigdy nie używaj IE. Przepraszam.
NawaMan

Tak, ale zgodnie z moim doświadczeniem z JavaScript, używałbym zmiennych globalnych tylko dla przestrzeni nazw modułów i ujawniał tylko to, co jest potrzebne (jak kod inicjujący, niektóre właściwości), jak pokazano tutaj . Zbyt łatwo jest dojść do konfliktów nazw w złożonych projektach ... spędzając wiele godzin, a następnie dowiadując się, że użyłeś już zmiennej w innym miejscu w projekcie.
Matt

Możesz pominąć var.
Timo

12

Oto bardzo pospieszny dowód słuszności koncepcji.

Jestem pewien, że są co najmniej 2 miejsca, w których można wprowadzić ulepszenia, i jestem też pewien, że nie przetrwało to długo na wolności. Mile widziane są wszelkie uwagi, które sprawią, że będzie on bardziej reprezentacyjny lub użyteczny.

Kluczem jest ustawienie identyfikatora dla elementu skryptu. Jedynym haczykiem jest to, że oznacza to, że możesz wywołać skrypt tylko raz, ponieważ szuka on tego identyfikatora, aby pobrać ciąg zapytania. Można to naprawić, gdyby zamiast tego skrypt przechodził przez wszystkie elementy zapytania, aby sprawdzić, czy któryś z nich do niego wskazuje, a jeśli tak, używa ostatniej instancji takiego elementu skryptu. Tak czy inaczej, z kodem:

Wywoływany skrypt:

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

Skrypt wywołany przez następującą stronę:

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

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

więc eval faktycznie ustawi wartość jako zmienną globalną ... całkiem fajnie.
rodmjay,

1
eval is evil, uciekaj
Barry Chapman

@barrychapman - o rany, 2010. Dziwię się, że nie było tam 10 innych czerwonych flag.
Anthony

9

może być bardzo proste

na przykład

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

Następnie możesz przekonwertować ciąg zapytania na json, jak poniżej

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

a następnie może użyć podobnych

console.log(json.id);

6

Można to łatwo zrobić, jeśli używasz jakiegoś frameworka Javascript, takiego jak jQuery. Tak jak tak

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

Teraz możesz użyć tych parametrów, dzieląc je jako parametry zmiennych.

Ten sam proces można wykonać bez żadnego frameworka, ale zajmie to trochę więcej linii kodu.


2
Jest też wtyczka jQuery o nazwie parseQuery, która jeszcze bardziej to ułatwia: plugins.jquery.com/project/parseQuery
Jimmy Cuadra

@JimmyCuadra - niestety podany przez Ciebie link jest uszkodzony. Ale znalazłem podobne podejście w samym jQuery: jQuery.param ()
Matt

1

Cóż, mógłbyś mieć plik javascript budowany przez dowolny język skryptowy, wstrzykując swoje zmienne do pliku przy każdym żądaniu. Musiałbyś powiedzieć swojemu serwerowi WWW, aby nie rozdzielał plików js statycznie (wystarczy użyć mod_rewrite).

Pamiętaj jednak, że tracisz buforowanie tych plików js, ponieważ są one stale zmieniane.

PA.


1
Nie mogę zrozumieć, dlaczego ten głos został odrzucony. Jest to całkowicie poprawne rozwiązanie wykorzystujące logikę po stronie serwera. O ile widzę, OP nie wymaga, aby było to w pełni javascriptowe rozwiązanie.
Anoyz,

@aefxx - Masz rację. Wewnątrz <script>bloku możesz użyć czegoś takiego, MyModule.Initialize( '<%: Model.SomeProperty%>', '<%: Model.SomeOtherProperty%>', ...);co jest renderowane na serwerze. Uwaga: <%:ucieka przed wartością, podczas gdy <%=wysyła wartość bez zmiany znaczenia. MyModulebyłaby przestrzenią nazw (tj. zmienną wewnątrz pliku .js, która jest samoczynnie wywoływana, co uwidacznia funkcję Initialize). Głosowałem za twoją odpowiedzią, ponieważ jest to pomocny wkład.
Matt

Uwaga: Pojęcie, jak utworzyć moduł, który zawiera odsłoniętą właściwość jest opisany bardziej szczegółowo tutaj na tej stronie.
Matt

1

Ładne pytanie i kreatywne odpowiedzi, ale moją sugestią jest sparametryzowanie twoich metod, co powinno rozwiązać wszystkie twoje problemy bez żadnych sztuczek.

jeśli masz funkcję:

function A()
{
    var val = external_value_from_query_string_or_global_param;
}

możesz to zmienić na:

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

Myślę, że jest to najbardziej naturalne podejście, nie musisz tworzyć dodatkowej dokumentacji na temat „parametrów plików” i otrzymujesz to samo. Jest to szczególnie przydatne, jeśli pozwolisz innym programistom na używanie Twojego pliku js.


0

Nie, naprawdę nie możesz tego zrobić, dodając zmienne do części z zapytaniami w adresie URL pliku JS. Jeśli zapisuje część kodu, aby przeanalizować ciąg, który ci przeszkadza, być może innym sposobem byłoby zakodowanie zmiennych w formacie json i umieszczenie ich w czymś podobnym do atrybutu rel tagu? Nie wiem, jak ważne jest to pod względem walidacji HTML, jeśli jest to coś, o co bardzo się martwisz. Następnie wystarczy znaleźć atrybut rel skryptu, a następnie json_decode.

na przykład

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

2
Możesz to zrobić za pomocą fałszywego querystringu. To po prostu nie jest dobry pomysł. Ale to też nie jest. Atrybut rel ma na celu określenie typu powiązania łącza, a nie wrzucanie losowych danych do znacznika skryptu.
Matthew Flaschen

1
^^ zgodził się. Nie widzę żadnych problemów z pozostawieniem go takim, jakim jest, i użyciem podejścia <script> var obj1 = "somevalue" </script>, jest to najlepszy sposób i uważam, że - właściwy.
Dal Hundal

0

To nie jest prawidłowy kod HTML (nie sądzę), ale wydaje się, że działa, jeśli utworzysz niestandardowy atrybut dla tagu script na swojej stronie internetowej :

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

Następnie uzyskaj dostęp do atrybutu niestandardowego w javascript:

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

Nie jestem pewien, czy jest to lepsze, czy gorsze niż analiza ciągu źródłowego skryptu.


0

Jeśli potrzebujesz sposobu, który przechodzi sprawdzanie CSP (który zabrania niebezpiecznego inline), musisz użyć metody nonce, aby dodać unikalną wartość zarówno do skryptu, jak i dyrektywy CSP lub zapisać swoje wartości w html i przeczytać je ponownie.

Metoda nonce dla 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>`)
})

lub zapisz wartości do metody html. w tym przypadku przy użyciu Jquery:

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


$(document).ready(() => {
    globalThis.EMAIL = $('#account').data('email');
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.