Melewati parameter ke file JavaScript

163

Seringkali saya akan memiliki file JavaScript yang ingin saya gunakan yang mengharuskan variabel tertentu didefinisikan di halaman web saya.

Jadi kodenya adalah seperti ini:

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

Tapi yang ingin saya lakukan adalah:

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

Saya mencoba berbagai metode dan yang terbaik adalah mengurai string kueri seperti ini:

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

Dan kemudian mencari nilai-nilai saya.

Saya bertanya-tanya apakah ada cara lain untuk melakukan ini tanpa membangun fungsi untuk mengurai string saya.

Apakah Anda semua tahu metode lain?

RaduM
sumber
9
Ini adalah pertanyaan rangkap ( stackoverflow.com/questions/1017424/… ) dan menurut saya desain yang salah arah. Jauh lebih mudah untuk menggunakan solusi variabel pertama Anda (oke), atau minta skrip mendefinisikan fungsi yang kemudian dipanggil dengan variabel (lebih baik).
Matthew Flaschen
Saya mencari solusi terbaru yang memanfaatkan fungsi ECMAScript 6 baru ... jika ada
Chef_Code
Menggunakan skrip internal apa pun akan memicu laporan CSP jika situs HTTPS dan jika CSP diaktifkan sepenuhnya. Alasan yang diberikan dalam dokumen CSP adalah memungkinkan elemen internal dapat membuat injeksi lebih mudah.
David Spector

Jawaban:

207

Saya akan merekomendasikan tidak menggunakan variabel global jika memungkinkan. Gunakan namespace dan OOP untuk meneruskan argumen Anda ke suatu objek.

Kode ini milik 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]);
        }
    };
}());

Dan di file html Anda:

<script type="text/javascript" src="file.js"></script>
<script type="text/javascript">
   MYLIBRARY.init(["somevalue", 1, "controlId"]);
   MYLIBRARY.helloWorld();
</script>
Naeem Sarfraz
sumber
4
@Naeem - adalah file.js dimuat 100% dari saat MYLIBRARY.init (["somevalue", 1, "controlId"]); dieksekusi? Mungkin kita perlu dokumen siap untuk meletakkannya?
Darius.V
2
1) Jika MYLIBRARY ada, gunakan atau tentukan objek baru. Maksudnya adalah untuk membuat namespace global, biasanya saya akan menggunakan sub namespace sehingga baris ini membantu untuk menghindari mendefinisikan ulang namespace tingkat atas. 2) Ya, itu telah menciptakan singleton.
Naeem Sarfraz
1
Saya telah membuat contoh dari ide ini: http://plnkr.co/edit/iE0Vr7sszfqrrDIsR8Wi?p=preview
robe007
1
Apa arti ungkapan ini? var MYLIBRARY = MYLIBRARY || (function(){}());? Mengapa harus MYLIBRARYdiatur ke nilai boolean?
Alex
1
@Alexander, lihat komentar saya di atas stackoverflow.com/questions/2190801/…
Naeem Sarfraz
79

Anda dapat melewatkan parameter dengan atribut arbitrer. Ini berfungsi di semua browser terbaru.

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

Di dalam somefile.js Anda bisa melewati nilai variabel dengan cara ini:

........

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

... dll ...

Vlado
sumber
1
Solusi yang sangat bagus, ini bahkan berfungsi dengan atribut async.
ViRuSTriNiTy
2
lama tetapi bagi mereka yang googling: ini bagus jika Anda perlu menggunakan CSP (kebijakan keamanan konten). Kami menggunakan banyak skrip tempat kami meneruskan string yang ditentukan dari sumber daya bahasa dan kunci API dll ke dalam file JS.
monkeySeeMonkeyDo
2
Perhatikan bahwa Anda juga dapat menggunakan document.currentScript, lihat caniuse.com/#feat=document-currentscript untuk kompatibilitas (atau gunakan polyfill)
Yves M.
The $(..)sintaks jquery, jangan lupa untuk memasukkannya.
Timo
48

Ide lain saya datang di menugaskan sebuah idke <script>elemen dan melewati argumen sebagai data-*atribut. <script>Tag yang dihasilkan akan terlihat seperti ini:

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

Script kemudian dapat menggunakan id untuk menemukan dan mem-parsing argumen secara terprogram. Diberi <script>tag sebelumnya , namanya dapat diambil seperti ini:

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

Kami mendapatkan name=helper

Nalini Wanjale
sumber
4
Jika Anda tidak ingin bergantung pada atribut id, Anda juga dapat membuat helper.jsloop skrip Anda melalui semua scripttag pada halaman, mencari yang dengan scr="helper.js", dan kemudian mengekstrak data-name.
jvannistelrooy
1
@ jvannistelrooy Saya belum mencoba tetapi saya yakin orang harus dapat mengakses skrip melalui jquery dan pemilih yang pintar seperti ini: var this_js_script = $('script[src*=somefile]');(disalin dari atas )
LosManos
19

Lihatlah URL ini. Ini berfungsi dengan baik untuk kebutuhan.

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

Terima kasih banyak kepada penulis. Untuk referensi cepat saya tempelkan logika utama di bawah ini:

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;
}
pengguna378221
sumber
1
Saya menyukai solusi ini, tetapi memodifikasinya sedikit, untuk menggunakan pengidentifikasi fragmen, sehingga tidak merusak cache: var frag = myScript.src.replace (/ ^ [^ \ #] + \ #? /, '') ;
Mark Nottingham
Oh, dan saya menemukan bahwa menempatkan JSON di sana berfungsi, selama Anda urlencode itu.
Mark Nottingham
@ user378221 - apakah Anda yakin ini akan selalu mendapatkan file yang tepat? Maksud saya ini mengasumsikan bahwa file ini adalah yang terakhir. Bagaimana jika lebih banyak tag skrip dimuat sebelum ini dijalankan? Saya tidak tahu apakah itu mungkin, tetapi saya menganggap dokumen tidak menunggu eksekusi kode selesai memuat tag lain?
Darius.V
Lebih baik menggunakan Use decodeURI () atau decodeURICon component () daripada unescape karena fungsinya sudah usang.
Steve Childs
@ Darius.V Ini berfungsi untuk skrip non-async. Saya lebih suka menggunakan opsi 1 dengan opsi 5 sebagai cadangan .
Lukas
14

Anda menggunakan variabel Global :-D.

Seperti ini:

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

Kode JavaScript di 'file.js' dapat mengakses obj1dan obj2tanpa masalah.

EDIT Hanya ingin menambahkan bahwa jika 'file.js' ingin memeriksa apakah obj1dan obj2bahkan telah dinyatakan, Anda dapat menggunakan fungsi berikut.

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

Semoga ini membantu.

NawaMan
sumber
Itu akan menimbulkan kesalahan yang tidak direferensikan di IE. Tubuh yang disukai dari fungsi itu adalah return typeof window[$Name] !== 'undefined';. Harap dicatat bahwa Anda akan meneruskan fungsi itu string yang berisi nama variabel. Tampaknya lebih mudah untuk memeriksa apakah variabel ada dalam kode panggilan (ini tidak dapat diimplementasikan sebagai fungsi) melalui if (typeof var !== 'undefined').
Anthony DiSanti
2
Oh, jangan pernah gunakan IE. Maaf.
NawaMan
Ya, tetapi sesuai pengalaman saya dengan JavaScript, saya akan menggunakan variabel global hanya untuk ruang nama modul dan mengekspos sebanyak yang diperlukan (seperti kode init, beberapa properti), seperti yang ditunjukkan di sini . Terlalu mudah untuk mendapatkan konflik penamaan dalam proyek kompleks ... menghabiskan waktu Anda dan kemudian mengetahui bahwa Anda telah menggunakan variabel di tempat lain dalam proyek.
Matt
Anda dapat menghilangkan var.
Timo
12

Inilah bukti konsep yang sangat terburu-buru.

Saya yakin setidaknya ada 2 tempat di mana ada perbaikan, dan saya juga yakin bahwa ini tidak akan bertahan lama di alam liar. Setiap umpan balik untuk membuatnya lebih rapi atau dapat digunakan dipersilahkan.

Kuncinya adalah mengatur id untuk elemen skrip Anda. Satu-satunya tangkapan adalah bahwa ini berarti Anda hanya dapat memanggil skrip sekali karena mencari ID untuk menarik string kueri. Ini bisa diperbaiki jika, sebaliknya, skrip loop melalui semua elemen permintaan untuk melihat apakah salah satu dari mereka menunjuk ke sana, dan jika demikian, menggunakan contoh terakhir dari elemen skrip tersebut. Bagaimanapun, lanjutkan dengan kode:

Naskah dipanggil:

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 dipanggil melalui halaman berikut:

<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
sumber
jadi eval sebenarnya akan menetapkan nilai sebagai variabel global ... cukup keren.
rodmjay
1
eval jahat lari
Barry Chapman
@barrychapman - oh man, 2010. Saya terkejut tidak ada 10 bendera merah lainnya di sana.
Anthony
9

mungkin sangat sederhana

sebagai contoh

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

Anda kemudian dapat mengubah string kueri menjadi json seperti di bawah ini

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

dan kemudian bisa menggunakan like

console.log(json.id);
Tofik
sumber
6

Ini dapat dengan mudah dilakukan jika Anda menggunakan beberapa kerangka kerja Javascript seperti jQuery. Seperti itu,

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

Sekarang Anda dapat menggunakan params ini dengan memisahkan sebagai parameter variabel Anda.

Proses yang sama dapat dilakukan tanpa kerangka kerja tetapi akan mengambil beberapa baris kode lagi.

GeekTantra
sumber
2
Ada juga plugin jQuery yang disebut parseQuery yang membuat ini lebih mudah: plugins.jquery.com/project/parseQuery
Jimmy Cuadra
@JimmyCuadra - sayangnya, tautan yang Anda berikan rusak. Tapi saya menemukan pendekatan serupa di jQuery sendiri: jQuery.param ()
Matt
1

Nah, Anda bisa membuat file javascript sedang dibangun oleh salah satu bahasa scripting, menyuntikkan variabel Anda ke file pada setiap permintaan. Anda harus memberi tahu server web Anda untuk tidak membagikan file js secara statis (menggunakan mod_rewrite sudah cukup).

Berhati-hatilah karena Anda kehilangan cache apa pun dari file-file js ini karena mereka terus-menerus diubah.

Sampai jumpa.

aefxx
sumber
1
Tidak dapat mengerti mengapa ini diturunkan. Ini adalah solusi yang sangat valid dengan menggunakan logika sisi server. Sejauh yang saya lihat, OP tidak mengharuskannya untuk menjadi solusi javascript sepenuhnya.
Anoyz
@ aefxx - Anda benar. Di dalam <script>blok Anda dapat menggunakan sesuatu seperti MyModule.Initialize( '<%: Model.SomeProperty%>', '<%: Model.SomeOtherProperty%>', ...);yang diberikan di server. Perhatian: <%:lolos dari nilai, sementara <%=mengirim nilai tidak terhapus. MyModuleakan menjadi namespace (yaitu variabel di dalam file .js yang dipanggil sendiri, yang mengekspos fungsi Initialize). Saya membatalkan jawaban Anda, karena ini merupakan kontribusi yang bermanfaat.
Matt
Catatan: Konsep, cara membuat Modul yang berisi properti yang terbuka dijelaskan secara lebih rinci di sini di halaman ini.
Matt
1

Pertanyaan yang bagus dan jawaban yang kreatif tetapi suggetion saya adalah untuk membuat metode Anda paramterized dan yang akan menyelesaikan semua masalah Anda tanpa ada trik.

jika Anda memiliki fungsi:

function A()
{
    var val = external_value_from_query_string_or_global_param;
}

Anda dapat mengubah ini menjadi:

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

Saya pikir ini adalah pendekatan yang paling alami, Anda tidak perlu membuat dokumentasi tambahan tentang 'parameter file' dan Anda menerima yang sama. Ini berguna khusus jika Anda mengizinkan pengembang lain menggunakan file js Anda.

dzida
sumber
0

Tidak, Anda tidak dapat melakukan ini dengan menambahkan variabel ke bagian querystring dari URL file JS. Jika penulisan bagian kode untuk mem-parsing string yang mengganggu Anda, mungkin cara lain adalah dengan mengkode variabel Anda dan meletakkannya dalam sesuatu seperti atribut rel dari tag? Saya tidak tahu seberapa valid ini dalam hal validasi HTML, jika itu sesuatu yang Anda sangat khawatirkan. Maka Anda hanya perlu menemukan atribut rel dari skrip dan kemudian json_decode itu.

misalnya

<script type='text/javascript' src='file.js' rel='{"myvar":"somevalue","anothervar":"anothervalue"}'></script>
Dal Hundal
sumber
2
Anda dapat melakukannya dengan querystring palsu. Itu bukan ide yang bagus. Tapi ini juga tidak. Atribut rel dimaksudkan untuk menentukan jenis hubungan tautan, bukan menjejalkan data acak ke dalam tag skrip.
Matthew Flaschen
1
^^ setuju. Saya tidak dapat melihat masalah dengan membiarkannya apa adanya, dan menggunakan pendekatan <script> var obj1 = "somevalue" </script>, ini cara terbaik, dan saya percaya - cara yang benar.
Dal Hundal
0

Ini bukan html yang valid (saya rasa tidak) tetapi tampaknya berfungsi jika Anda membuat atribut khusus untuk tag skrip di halaman web Anda :

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

Kemudian akses atribut khusus di javascript:

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

Tidak yakin apakah ini lebih baik atau lebih buruk daripada mem-parsing string sumber skrip.

Kor
sumber
0

Jika Anda memerlukan cara yang melewati pemeriksaan CSP (yang melarang in-line tidak aman) maka Anda harus menggunakan metode nonce untuk menambahkan nilai unik ke skrip dan arahan CSP atau menulis nilai Anda ke dalam html dan membacanya lagi.

Metode Nonce untuk 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>`)
})

atau tulis nilai ke metode html. dalam hal ini menggunakan Jquery:

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


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