Передача параметрів у файли JavaScript


163

Часто у мене буде файл JavaScript, який я хочу використовувати, який вимагає, щоб певні змінні були визначені на моїй веб-сторінці.

Отже, код є приблизно таким:

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

Але я хочу зробити:

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

Я спробував різні методи, і найкращий з них - це проаналізувати рядок запитів таким чином:

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

А потім шукайте мої цінності.

Цікаво, чи є інший спосіб зробити це без побудови функції для розбору мого рядка.

Ви всі знаєте інші методи?


9
Це повторне запитання ( stackoverflow.com/questions/1017424/… ) і, на мій погляд, неправильна конструкція. Набагато простіше або використовувати своє перше рішення змінної (добре), або сценарій визначити функцію, яку потім викликають зі змінними (краще).
Метью Флашен

Я шукаю оновлене рішення, яке скористається новою функціональністю ECMAScript 6 ... якщо така є
Chef_Code

Використання будь-якого внутрішнього сценарію викликає звіти про CSP, якщо на сайті є HTTPS та якщо CSP повністю увімкнено. Причина, вказана в документі CSP, полягає в тому, що дозволити внутрішні елементи можуть полегшити ін'єкцію.
Девід Спектор

Відповіді:


207

Я рекомендую не використовувати глобальні змінні, якщо це можливо. Використовуйте простір імен та OOP, щоб передати свої аргументи об'єкту.

Цей код належить до 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]);
        }
    };
}());

І у вашому 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 завантажується у 100% часу, коли MYLIBRARY.init (["деяка ціна", 1, "controlId"]); отримує страту? Може, нам потрібен документ, готовий його скласти?
Дарій.V

2
1) Якщо MYLIBRARY існує, то використовуйте його або визначте новий об'єкт. Наміром є створення глобального простору імен, зазвичай я буду використовувати підпростори імен, тому цей рядок допомагає уникнути повторного визначення простору імен верхнього рівня. 2) Так, це створило синглтон.
Naeem Sarfraz

1
Я зробив приклад такої ідеї: http://plnkr.co/edit/iE0Vr7sszfqrrDIsR8Wi?p=preview
robe007

1
Що означає цей вираз? var MYLIBRARY = MYLIBRARY || (function(){}());? Чому слід MYLIBRARYвстановити булеве значення?
Алекс

1
@ Олександр, дивіться мій коментар вище stackoverflow.com/questions/2190801/…
Naeem Sarfraz

79

Ви можете передавати параметри з довільними атрибутами. Це працює у всіх останніх браузерах.

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

Всередині somefile.js ви можете передати значення змінних таким чином:

........

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

... і т.д. ...


1
Дуже приємне рішення, це навіть працює з атрибутом async.
ViRuSTriNiTy

2
старий, але для тих, хто гуглить: це чудово, якщо вам потрібно обійтися CSP (політика безпеки вмісту). Ми використовуємо безліч скриптів, в яких передаємо рядки, визначені з мовних ресурсів та ключів API тощо, у файли JS.
monkeySeeMonkeyDo

2
Зауважте, що ви також можете використовувати document.currentScript, дивіться caniuse.com/#feat=document-currentscript для сумісності (або використовувати поліфункцію)
Ів М.

$(..)Синтаксис JQuery, не забудьте включити його.
Тімо

48

Ще одна ідея , яку я натрапив був Призначивши idдо <script>елементу і передаючи аргументи як data-*атрибути. Отриманий <script>тег виглядатиме приблизно так:

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

Потім сценарій може використовувати ідентифікатор для програмного пошуку себе та розбору аргументів. З огляду на попередній <script>тег, ім’я можна отримати так:

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

Отримуємо name=helper


4
Якщо ви не хочете залежати від атрибута id, ви також можете зробити helper.jsцикл сценарію через усі scriptтеги на сторінці, шукаючи тег scr="helper.js", а потім витягнути data-name.
jvannistelrooy

1
@jvannistelrooy Я не пробував, але я вважаю, що треба мати можливість дістатися до сценарію через jquery та розумний селектор так: var this_js_script = $('script[src*=somefile]');(скопійовано зверху )
LosManos

19

Ознайомтеся з цією URL-адресою. Це прекрасно працює на вимогу.

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

Велике спасибі автору. Для швидкого ознайомлення я вставив основну логіку нижче:

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
Це рішення мені сподобалось, але його трохи змінили, щоб використовувати ідентифікатор фрагмента, щоб він не перетворював кеші: var frag = myScript.src.replace (/ ^ [^ \ #] + \ #? /, '') ;
Марк Ноттінгем

О, і я виявив, що введення JSON туди працює, доки ви не вкажете його.
Марк Ноттінгем

@ user378221 - ви впевнені, що це завжди отримає потрібний файл? Це означає, що цей файл останній. Що робити, якщо до запуску буде завантажено більше тегів сценарію? Я не знаю, чи можливо це, але я припускаю, що документ не чекає завершення виконання коду для завантаження інших тегів?
Дарій.V

Краще використовувати використовувати decodeURI () або decodeURIComponent () замість unescape, оскільки ця функція застаріла.
Стів Чайлдс

@ Darius.V Це працює для сценаріїв без асинхронізації. Я вважаю за краще використовувати варіант 1 із варіантом 5 як резервний .
Лука

14

Ви використовуєте глобальні змінні :-D.

Подобається це:

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

Код JavaScript у файлі 'file.js' може отримати доступ obj1і obj2без проблем.

EDIT Просто хочу додати, що якщо 'file.js' хоче перевірити, чи був obj1і obj2навіть оголошений, ви можете використовувати наступну функцію.

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

Сподіваюся, це допомагає.


Це призведе до невстановленої помилки в IE. Кращим органом цієї функції є return typeof window[$Name] !== 'undefined';. Зауважте, що ви передали б цій функції рядок, що містить ім'я змінної. Ймовірно, простіше просто перевірити, чи існує змінна в коді виклику (це неможливо реалізувати як функцію) через if (typeof var !== 'undefined').
Ентоні Дісанті

2
О, ніколи не використовуйте IE. Вибачте.
NawaMan

Так, але, згідно з моїм досвідом роботи з JavaScript, я б використовував глобальні змінні лише для просторів імен модулів і викривав би стільки, скільки потрібно (наприклад, код init, деякі властивості), як показано тут . Занадто просто отримати конфлікти іменування в складних проектах ... витрачати години свого часу, а потім з’ясувати, що ви вже використовували змінну десь в іншому проекті.
Метт

Ви можете опустити var.
Тімо

12

Ось дуже поспішний доказ концепції.

Я впевнений, що є щонайменше два місця, де можуть бути покращення, і я також впевнений, що це не витримає довго в дикій природі. Будь-який відгук, щоб зробити його більш презентабельним або зручним для використання, вітається.

Ключ - це встановлення ідентифікатора для вашого елемента сценарію. Єдина уловка полягає в тому, що це означає, що ви можете викликати скрипт лише один раз, оскільки він шукає цей ідентифікатор для витягування рядка запиту. Це може бути виправлено, якщо натомість скрипт проходить через усі елементи запиту, щоб побачити, чи хтось із них вказує на нього, і якщо так, використовується останній екземпляр такого елемента сценарію. У будь-якому випадку, з кодом:

Викликається сценарій:

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 id="myScript" type="text/javascript" 
        src="test.js?header=Test Page&text=This Works"></script>

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

тому eval насправді встановить значення як глобальну змінну ... досить круто.
rodmjay

1
eval is зло бігти
Barry Chapman

@barrychapman - о, чоловіче, 2010. Я дивуюсь, що там не було 10 інших червоних прапорів.
Ентоні

9

може бути дуже простим

наприклад

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

Потім ви можете перетворити рядок запиту в json, як показано нижче

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

а потім може використовувати подібні

console.log(json.id);

6

Це легко зробити, якщо ви використовуєте таку основу Javascript, як jQuery. Так,

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

Тепер ви можете використовувати ці параметри, розділяючи їх як ваші змінні параметри.

Цей же процес можна виконати без будь-яких фреймворків, але буде потрібно ще кілька рядків коду.


2
Також є плагін jQuery під назвою parseQuery, що робить це ще простіше: plugins.jquery.com/project/parseQuery
Jimmy Cuadra

@JimmyCuadra - на жаль, посилання, яке ви надали, порушено. Але я знайшов подібний підхід у самому jQuery: jQuery.param ()
Метт

1

Ну, ви можете створити файл JavaScript, створений будь-якою із мов скриптів, вводячи свої змінні у файл на кожен запит. Вам доведеться сказати своєму веб-серверу, щоб не передавав js-файли статично (достатньо використання mod_rewrite).

Будьте в курсі, що ви втрачаєте кешування цих js-файлів, оскільки вони постійно змінюються.

До побачення.


1
Не можу зрозуміти, чому це було знято. Це ідеально правильне рішення, яке вдається до логіки на стороні сервера. Наскільки я бачу, ОП не вимагає, щоб це було повністю JavaScript-рішення.
Анойз

@aefxx - Ви маєте рацію. Всередині <script>блоку ви можете використовувати щось на зразок, MyModule.Initialize( '<%: Model.SomeProperty%>', '<%: Model.SomeOtherProperty%>', ...);що відображається на сервері. Застереження: <%:уникає значення, при цьому <%=надсилає значення без нагляду. MyModuleбуде простором імен (тобто змінною всередині .js-файлу, який викликається самостійно, що відкриває функцію Initialize). Я підтримав вашу відповідь, оскільки це корисний внесок.
Метт

Примітка . Поняття про те, як створити модуль, який містить відкриту властивість, більш докладно описано тут, на цій сторінці.
Метт

1

Приємне запитання та творчі відповіді, але моя пропозиція полягає в тому, щоб зробити свої методи переосмисленими і це повинно вирішити всі ваші проблеми без жодних хитрощів.

якщо у вас є функція:

function A()
{
    var val = external_value_from_query_string_or_global_param;
}

Ви можете змінити це на:

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

Я думаю, що це найбільш природний підхід, вам не потрібно складати додаткову документацію про "параметри файлу", і ви отримуєте те саме. Це особливо корисно, якщо ви дозволяєте іншим розробникам використовувати ваш js-файл.


0

Ні, ви дійсно не можете це зробити, додавши змінні до частини запиту рядка URL-адреси файлу JS. Якщо його написання частини коду для розбору рядка, який вас турбує, можливо, іншим способом було б json кодувати ваші змінні та помістити їх у щось на зразок атрибута rel тегу? Я не знаю, наскільки це справедливо з точки зору перевірки HTML, якщо це те, про що ви дуже переживаєте. Тоді вам просто потрібно знайти атрибут rel скрипту, а потім json_decode, що.

напр

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

2
Ви можете зробити це за допомогою підробленого запиту. Це просто не гарна ідея. Але це також не є. Атрибут rel призначений для визначення типу зв’язку, а не набивання випадкових даних у тег сценарію.
Метью Флашен

1
^^ погодився. Я не бачу проблем із тим, щоб залишити його таким, яким він є, і використовувати підхід <script> var obj1 = "деяке значення" </script> - найкращий спосіб, і я вважаю - правильний шлях.
Дал Хундал

0

Це недійсний html (я не думаю), але він, здається, працює, якщо створити спеціальний атрибут для тегу сценарію на своїй веб-сторінці :

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

Потім відкрийте спеціальний атрибут у javascript:

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

Не впевнений, що це краще чи гірше, ніж розбирати рядок джерела сценарію.


0

Якщо вам потрібен спосіб, який передає перевірку CSP (що забороняє небезпечний вбудований), тоді вам доведеться використовувати метод nonce, щоб додати унікальне значення як сценарію, так і директиві CSP або записати свої значення в html та прочитати їх ще раз.

Метод nonce для 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>`)
})

або записати значення в html-метод. у цьому випадку за допомогою Jquery:

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


$(document).ready(() => {
    globalThis.EMAIL = $('#account').data('email');
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.