Truyền tham số cho tệp JavaScript


163

Thường thì tôi sẽ có một tệp JavaScript mà tôi muốn sử dụng yêu cầu một số biến nhất định được xác định trong trang web của tôi.

Vì vậy, mã là một cái gì đó như thế này:

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

Nhưng điều tôi muốn làm là:

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

Tôi đã thử các phương thức khác nhau và cách tốt nhất là phân tích chuỗi truy vấn như thế này:

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

Và sau đó tìm kiếm các giá trị của tôi.

Tôi tự hỏi nếu có một cách khác để làm điều này mà không xây dựng một hàm để phân tích chuỗi của tôi.

Bạn có biết các phương pháp khác?


9
Đây là một câu hỏi trùng lặp ( stackoverflow.com/questions/1017424/ - ) và theo tôi là một thiết kế sai lầm. Nó đơn giản hơn nhiều khi sử dụng giải pháp biến đầu tiên của bạn (được) hoặc có tập lệnh xác định một hàm sau đó được gọi với các biến (tốt hơn).
Matthew Flaschen

Tôi đang tìm kiếm một giải pháp cập nhật tận dụng chức năng ECMAScript 6 mới ... nếu có
Chef_Code

Sử dụng bất kỳ tập lệnh nội bộ nào sẽ kích hoạt báo cáo CSP nếu trang web là HTTPS và nếu CSP được bật hoàn toàn. Lý do được đưa ra trong tài liệu CSP là cho phép các yếu tố bên trong có thể giúp tiêm dễ dàng hơn.
David Spector

Câu trả lời:


207

Tôi khuyên bạn không nên sử dụng các biến toàn cục nếu có thể. Sử dụng một không gian tên và OOP để truyền các đối số của bạn cho một đối tượng.

Mã này thuộc về 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]);
        }
    };
}());

Và trong tệp html của bạn:

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

4
@Naeem - là file.js được tải 100% thời gian khi MYLIBRARY.init (["somevalue", 1, "controlId"]); bị xử tử? Có lẽ chúng ta cần trên tài liệu đã sẵn sàng để đặt nó?
Darius.V

2
1) Nếu MYLIBRARY tồn tại thì sử dụng nó hoặc xác định một đối tượng mới. Mục đích là để tạo ra một không gian tên toàn cầu, thông thường tôi sẽ sử dụng các không gian tên phụ để dòng này giúp tránh xác định lại không gian tên cấp cao nhất. 2) Có, nó đã tạo ra một singleton.
Naeem Sarfraz

1
Tôi đã làm một ví dụ về ý tưởng này: http://plnkr.co/edit/iE0Vr7sszfqrrDIsR8Wi?p=preview
robe007 7/12/2015

1
Biểu hiện này có ý nghĩa gì? var MYLIBRARY = MYLIBRARY || (function(){}());? Tại sao nên MYLIBRARYđược đặt thành một giá trị boolean?
Alex

1
@Alexander, xem nhận xét của tôi ở trên stackoverflow.com/questions/2190801/ từ
Naeem Sarfraz

79

Bạn có thể truyền tham số với các thuộc tính tùy ý. Điều này hoạt động trong tất cả các trình duyệt gần đây.

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

Bên trong somefile.js bạn có thể nhận các giá trị biến được chuyển theo cách này:

........

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

...Vân vân...


1
Giải pháp rất hay, điều này thậm chí hoạt động với thuộc tính async.
ViRuSTriNiTy

2
cũ nhưng đối với những người này: điều này thật tuyệt nếu bạn cần làm việc với CSP (chính sách bảo mật nội dung). Chúng tôi sử dụng rất nhiều tập lệnh trong đó chúng tôi chuyển các chuỗi được xác định từ tài nguyên ngôn ngữ và khóa API, v.v. vào các tệp JS.
khỉSeeMonkeyDo

2
Lưu ý rằng bạn cũng có thể sử dụng document.currentScript, xem caniuse.com/#feat=document-currentscript để biết tính tương thích (hoặc sử dụng polyfill)
Yves M.

Các $(..)cú pháp là jquery, đừng quên để bao gồm nó.
Timo

48

Một ý tưởng tôi tình cờ được gán một idđến <script>phần tử và đi qua các đối số là data-*các thuộc tính. <script>Thẻ kết quả sẽ trông giống như thế này:

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

Kịch bản sau đó có thể sử dụng id để lập trình định vị chính nó và phân tích các đối số. Cho <script>thẻ trước , tên có thể được lấy như thế này:

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

Chúng tôi get name=helper


4
Nếu bạn không muốn phụ thuộc vào thuộc tính id, bạn cũng có thể tạo helper.jsvòng lặp tập lệnh của mình thông qua tất cả scriptcác thẻ trên trang, tìm kiếm với scr="helper.js", sau đó giải nén data-name.
jvannistelrooy

1
@jvannistelrooy Tôi chưa thử nhưng tôi tin rằng người ta có thể truy cập vào kịch bản thông qua jquery và một bộ chọn thông minh như vậy: var this_js_script = $('script[src*=somefile]');(sao chép từ trên )
LosManos

19

Kiểm tra URL này. Nó đang làm việc hoàn hảo cho các yêu cầu.

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

Cảm ơn rất nhiều đến tác giả. Để tham khảo nhanh, tôi đã dán logic chính bên dưới:

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
Tôi thích giải pháp này, nhưng đã sửa đổi nó một chút, để sử dụng mã định danh phân đoạn, để nó không phá vỡ bộ đệm: var Frag = myScript.src.replace (/ ^ [^ \ #] + \ #? /, '') ;
Đánh dấu

Ồ, và tôi thấy rằng việc đưa JSON vào đó hoạt động, miễn là bạn urlencode nó.
Đánh dấu

@ user378221 - bạn có chắc chắn điều này sẽ luôn lấy đúng tệp? Tôi có nghĩa là điều này giả định rằng tập tin này là cuối cùng. Điều gì nếu nhiều thẻ script được tải trước khi thực thi? Tôi không biết nếu điều đó là có thể, nhưng tôi giả sử tài liệu không đợi quá trình thực thi mã hoàn tất để tải các thẻ khác?
Darius.V

Tốt hơn nên sử dụng Sử dụng decodeURI () hoặc decodeURIComponent () thay vì unescape vì chức năng đó không được dùng nữa.
Steve Childs

@ Darius.V Điều này hoạt động cho các kịch bản không đồng bộ. Tôi thích sử dụng tùy chọn 1 với tùy chọn 5 làm dự phòng .
Lu-ca

14

Bạn sử dụng biến toàn cục :-D.

Như thế này:

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

Mã JavaScript trong 'file.js' có thể truy cập obj1obj2không gặp sự cố.

EDIT Chỉ muốn thêm rằng nếu 'file.js' muốn kiểm tra xem obj1obj2thậm chí đã được khai báo hay chưa, bạn có thể sử dụng chức năng sau.

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

Hi vọng điêu nay co ich.


Điều đó sẽ đưa ra một lỗi không thể kiểm tra trong IE. Cơ thể ưa thích của chức năng đó là return typeof window[$Name] !== 'undefined';. Xin lưu ý rằng bạn sẽ truyền vào hàm đó một chuỗi chứa tên của biến. Có thể dễ dàng hơn để kiểm tra xem biến có tồn tại trong mã gọi hay không (điều này không thể được thực hiện như một hàm) thông qua if (typeof var !== 'undefined').
Anthony DiSanti

2
Ồ, đừng bao giờ sử dụng IE. Lấy làm tiếc.
NawaMan

Có, nhưng theo kinh nghiệm của tôi với JavaScript, tôi sẽ chỉ sử dụng các biến toàn cục cho các không gian tên mô-đun và hiển thị nhiều như những gì cần thiết (như mã init, một số thuộc tính), như được hiển thị ở đây . Thật quá dễ dàng để có được xung đột đặt tên trong các dự án phức tạp ... dành hàng giờ thời gian của bạn và sau đó phát hiện ra bạn đã sử dụng biến ở một nơi khác trong dự án.
Matt

Bạn có thể bỏ qua var.
Timo

12

Dưới đây là một bằng chứng rất vội vàng của khái niệm.

Tôi chắc chắn có ít nhất 2 nơi có thể cải thiện và tôi cũng chắc chắn rằng điều này sẽ không tồn tại lâu trong tự nhiên. Bất kỳ thông tin phản hồi để làm cho nó dễ trình bày hơn hoặc có thể sử dụng được hoan nghênh.

Chìa khóa là thiết lập id cho thành phần script của bạn. Điều hấp dẫn duy nhất là điều này có nghĩa là bạn chỉ có thể gọi tập lệnh một lần vì nó tìm ID đó để kéo chuỗi truy vấn. Điều này có thể được sửa nếu, thay vào đó, tập lệnh lặp qua tất cả các phần tử truy vấn để xem liệu có bất kỳ phần tử nào trỏ đến nó không, và nếu vậy, sử dụng phiên bản cuối cùng của phần tử tập lệnh đó. Dù sao, với mã:

Kịch bản được gọi là:

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 được gọi thông qua trang sau:

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

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

Vì vậy, eval thực sự sẽ đặt giá trị là biến toàn cục ... khá tuyệt.
Rodmjay

1
eval là ác chạy đi
Barry Chapman

@barrychapman - oh man, 2010. Tôi ngạc nhiên khi không có 10 lá cờ đỏ khác ở đó.
Anthony

9

có thể rất đơn giản

ví dụ

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

Sau đó, bạn có thể chuyển đổi chuỗi truy vấn thành json như bên dưới

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

và sau đó có thể sử dụng như

console.log(json.id);

6

Điều này có thể dễ dàng thực hiện nếu bạn đang sử dụng một số khung Javascript như jQuery. Như vậy

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

Bây giờ bạn có thể sử dụng các thông số này bằng cách chia làm tham số biến của bạn.

Quá trình tương tự có thể được thực hiện mà không cần bất kỳ khuôn khổ nào nhưng sẽ mất thêm một số dòng mã.


2
Ngoài ra còn có một plugin jQuery được gọi là parseQuery giúp việc này trở nên dễ dàng hơn: plugins.jquery.com/project/parseQuery
Jimmy Cuadra

@JimmyCuadra - thật không may, liên kết bạn cung cấp bị hỏng. Nhưng tôi đã tìm thấy một cách tiếp cận tương tự trong chính jQuery: jQuery.param ()
Matt

1

Chà, bạn có thể có tệp javascript được xây dựng bởi bất kỳ ngôn ngữ script nào, đưa các biến của bạn vào tệp theo mỗi yêu cầu. Bạn sẽ phải yêu cầu máy chủ web của mình không xử lý các tệp js tĩnh (sử dụng mod_rewrite sẽ đủ).

Hãy lưu ý rằng bạn sẽ mất bất kỳ bộ nhớ đệm của các tệp js này vì chúng bị thay đổi liên tục.

Tạm biệt.


1
Không thể hiểu tại sao điều này đã bị hạ cấp. Đó là một giải pháp hoàn toàn hợp lệ dựa trên logic phía máy chủ. Theo như tôi thấy, OP không yêu cầu nó phải là một giải pháp javascript hoàn toàn.
Anoyz 7/12/2015

@aefxx - Bạn nói đúng. Trong <script>khối, bạn có thể sử dụng một cái gì đó giống như MyModule.Initialize( '<%: Model.SomeProperty%>', '<%: Model.SomeOtherProperty%>', ...);được hiển thị trên máy chủ. Thận trọng: <%:thoát khỏi giá trị, trong khi <%=gửi giá trị không thoát. MyModulesẽ là một không gian tên (tức là một biến trong tệp .js tự gọi, để lộ hàm Initialize). Tôi nêu lên câu trả lời của bạn, bởi vì đó là một đóng góp hữu ích.
Matt

Lưu ý: Khái niệm, cách tạo Mô-đun chứa thuộc tính được hiển thị được mô tả chi tiết hơn tại đây trên trang này.
Matt

1

Câu hỏi hay và câu trả lời sáng tạo nhưng điều tuyệt vời của tôi là làm cho phương pháp của bạn được tối ưu hóa và điều đó sẽ giải quyết tất cả các vấn đề của bạn mà không cần bất kỳ thủ thuật nào.

nếu bạn có chức năng:

function A()
{
    var val = external_value_from_query_string_or_global_param;
}

bạn có thể thay đổi điều này thành:

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

Tôi nghĩ rằng đây là cách tiếp cận tự nhiên nhất, bạn không cần phải viết thêm tài liệu về 'tham số tệp' và bạn cũng nhận được như vậy. Điều này đặc biệt hữu ích nếu bạn cho phép các nhà phát triển khác sử dụng tệp js của bạn.


0

Không, bạn không thể thực sự làm điều này bằng cách thêm các biến vào phần chuỗi truy vấn của URL tệp JS. Nếu nó viết phần mã để phân tích chuỗi làm phiền bạn, có lẽ một cách khác là json mã hóa các biến của bạn và đặt chúng vào một cái gì đó giống như thuộc tính rel của thẻ? Tôi không biết điều này hợp lệ như thế nào về mặt xác thực HTML, nếu đó là điều bạn rất lo lắng. Sau đó, bạn chỉ cần tìm thuộc tính rel của script và sau đó json_decode đó.

ví dụ

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

2
Bạn có thể làm điều đó với chuỗi truy vấn giả. Đó không phải là một ý tưởng tốt. Nhưng đây không phải là. Thuộc tính rel có nghĩa là chỉ định một loại mối quan hệ liên kết, không nhồi nhét dữ liệu ngẫu nhiên vào thẻ script.
Matthew Flaschen

1
^^ đồng ý. Tôi không thể thấy bất kỳ vấn đề nào khi cứ để nguyên như vậy và sử dụng phương pháp <script> var obj1 = "somevalue" </ script>, đó là cách tốt nhất và tôi tin - đúng cách.
Dal Hundal

0

Nó không hợp lệ html (tôi không nghĩ vậy) nhưng có vẻ như nó hoạt động nếu bạn tạo một thuộc tính tùy chỉnh cho thẻ script trong trang web của bạn :

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

Sau đó truy cập thuộc tính tùy chỉnh trong javascript:

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

Không chắc chắn nếu điều này tốt hơn hay tồi tệ hơn là phân tích chuỗi nguồn script.


0

Nếu bạn cần một cách vượt qua kiểm tra CSP (cấm không an toàn nội tuyến) thì bạn phải sử dụng phương pháp nonce để thêm một giá trị duy nhất cho cả tập lệnh và lệnh CSP hoặc ghi giá trị của bạn vào html và đọc lại chúng.

Phương thức nonce cho 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>`)
})

hoặc ghi giá trị vào phương thức html. trong trường hợp này sử dụng Jquery:

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


$(document).ready(() => {
    globalThis.EMAIL = $('#account').data('email');
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.