Передача параметров в файлы 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();

А потом ищи мои ценности.

Интересно, есть ли другой способ сделать это без создания функции для анализа моей строки.

Вы все знаете другие методы?

RaduM
источник
9
Это дублирующий вопрос ( stackoverflow.com/questions/1017424/… ) и, на мой взгляд, ошибочный дизайн. Гораздо проще либо использовать решение для первой переменной (хорошо), либо попросить скрипт определить функцию, которая затем вызывается с переменными (лучше).
Мэтью Флэшен
Я ищу обновленное решение, которое использует преимущества новой функциональности ECMAScript 6 ... если есть
Chef_Code
Использование любого внутреннего скрипта вызовет отчеты CSP, если сайт HTTPS и если CSP полностью включен. Причина, приведенная в документе CSP, заключается в том, что использование внутренних элементов может упростить инъекцию.
Дэвид Спектор

Ответы:

207

Я бы порекомендовал не использовать глобальные переменные, если это возможно. Используйте пространство имен и ООП, чтобы передать свои аргументы объекту.

Этот код принадлежит в 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 (["somevalue", 1, "controlId"]); исполняется? Может нам нужен документ, готовый его поставить?
Darius.V
2
1) Если MYLIBRARY существует, используйте его или определите новый объект. Намерение состоит в том, чтобы создать глобальное пространство имен, обычно я буду использовать подпространства имен, так что эта строка помогает избежать переопределения пространства имен верхнего уровня. 2) Да, он создал синглтон.
Наим Сарфраз
1
Я сделал пример этой идеи: http://plnkr.co/edit/iE0Vr7sszfqrrDIsR8Wi?p=preview
robe007
1
Что означает это выражение? var MYLIBRARY = MYLIBRARY || (function(){}());? Почему должно MYLIBRARYбыть установлено логическое значение?
Алекс
1
@Alexander см мой комментарий выше stackoverflow.com/questions/2190801/...
Наим Сарфраза
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 для совместимости (или использовать polyfill)
Ив М.
$(..)Синтаксис 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;
}
user378221
источник
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);
}

Надеюсь это поможет.

NawaMan
источник
Это приведет к ошибке в IE. Предпочтительным телом этой функции является return typeof window[$Name] !== 'undefined';. Обратите внимание, что вы передадите этой функции строку, содержащую имя переменной. Вероятно, проще просто проверить, существует ли переменная в вызывающем коде (это не может быть реализовано как функция) через if (typeof var !== 'undefined').
Энтони ДиСанти
2
О, никогда не используйте IE. Сожалею.
NawaMan
Да, но, как показывает мой опыт работы с JavaScript, я бы использовал глобальные переменные только для пространств имен модулей и выставил столько, сколько нужно (например, код инициализации, некоторые свойства), как показано здесь . Слишком легко получить конфликты имен в сложных проектах ... тратить часы своего времени, а затем узнать, что вы уже использовали переменную где-то еще в проекте.
Мэтт
Вы можете опустить var.
Тимо
12

Вот очень поспешное доказательство концепции.

Я уверен, что есть как минимум 2 места, где могут быть улучшения, и я также уверен, что это не продержится долго в дикой природе. Любые отзывы, чтобы сделать его более презентабельным или пригодным для использования, приветствуются.

Ключ устанавливает идентификатор для вашего элемента скрипта. Единственный улов в том, что это означает, что вы можете вызвать скрипт только один раз, так как он ищет этот идентификатор, чтобы получить строку запроса. Это можно исправить, если вместо этого сценарий перебирает все элементы запроса, чтобы увидеть, указывает ли на них какой-либо из них, и, если это так, использует последний экземпляр такого элемента сценария. В любом случае, с кодом:

Сценарий называется:

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 - зло, убежало
Барри Чепмен
@ 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);
Tofeeq
источник
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

Теперь вы можете использовать эти параметры, разделяя их в качестве параметров вашей переменной.

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

GeekTantra
источник
2
Есть также плагин jQuery под названием parseQuery, который делает это еще проще: plugins.jquery.com/project/parseQuery
Джимми Куадра
@JimmyCuadra - к сожалению, указанная вами ссылка не работает. Но я нашел похожий подход в самом jQuery: jQuery.param ()
Мэтт
1

Ну, вы могли бы создать файл javascript любым из языков сценариев, вставляя ваши переменные в файл при каждом запросе. Вы должны сказать своему веб-серверу, чтобы он не выдавал js-файлы статически (достаточно использовать mod_rewrite).

Имейте в виду, что вы потеряете любое кэширование этих js-файлов, поскольку они постоянно изменяются.

До свидания.

aefxx
источник
1
Не могу понять, почему за это проголосовали. Это совершенно правильное решение, основанное на серверной логике. Насколько я вижу, OP не требует, чтобы это было полностью 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.

dzida
источник
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 = "somevalue" </ script>, это лучший способ, и я верю - правильный путь.
Даль Хундал
0

Это недействительный HTML (я не думаю), но, похоже, он будет работать, если вы создадите пользовательский атрибут для тега script на своей веб-странице :

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

Затем получите доступ к пользовательскому атрибуту в JavaScript:

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

Не уверен, что это лучше или хуже, чем анализ исходной строки скрипта.

Cor
источник
0

Если вам нужен способ, который проходит проверку CSP (которая запрещает unsafe-inline), то вам нужно использовать метод 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');
}
Дэвид Дехган
источник
0

Хотя этот вопрос был задан некоторое время назад, он все еще актуален на сегодняшний день. Это не тривиальный подход с использованием параметров файла сценария, но у меня уже было несколько крайних вариантов использования, которые наиболее подходили для этого способа.

Я наткнулся на этот пост, чтобы найти лучшее решение, чем писал некоторое время назад, с надеждой найти, возможно, нативную функцию или что-то подобное.
Я поделюсь своим решением, пока не будет реализовано лучшее. Это работает на большинстве современных браузеров, может быть, даже на старых, не пробовал.

Все решения, приведенные выше, основаны на том факте, что он должен быть внедрен с предопределенным и хорошо маркированным тегом SCRIPT и полностью полагаться на реализацию HTML. Но что, если скрипт внедряется динамически или, что еще хуже, что, если вы напишите библиотеку, которая будет использоваться на различных веб-сайтах?
В этих и некоторых других случаях все приведенные выше ответы являются недостаточными и даже становятся слишком сложными.

Во-первых, давайте попробуем понять, чего нам нужно достичь здесь. Все, что нам нужно сделать, это получить URL-адрес самого скрипта, оттуда это будет просто.

На самом деле есть хороший трюк, чтобы получить URL скрипта от самого скрипта. Одной из функциональных возможностей нативного Errorкласса является возможность обеспечения трассировки стека «проблемного местоположения», включая точную трассировку файла до последнего вызова. Чтобы добиться этого, я буду использовать свойство стека экземпляра Error, который после создания даст полную трассировку стека.

Вот как работает магия:

// The pattern to split each row in the stack trace string
const STACK_TRACE_SPLIT_PATTERN = /(?:Error)?\n(?:\s*at\s+)?/;
// For browsers, like Chrome, IE, Edge and more.
const STACK_TRACE_ROW_PATTERN1 = /^.+?\s\((.+?):\d+:\d+\)$/;
// For browsers, like Firefox, Safari, some variants of Chrome and maybe other browsers.
const STACK_TRACE_ROW_PATTERN2 = /^(?:.*?@)?(.*?):\d+(?::\d+)?$/;

const getFileParams = () => {
    const stack = new Error().stack;
    const row = stack.split(STACK_TRACE_SPLIT_PATTERN, 2)[1];
    const [, url] = row.match(STACK_TRACE_ROW_PATTERN1) || row.match(STACK_TRACE_ROW_PATTERN2) || [];
    if (!url) {
        console.warn("Something went wrong. You should debug it and find out why.");
        return;
    }
    try {
        const urlObj = new URL(url);
        return urlObj.searchParams; // This feature doesn't exists in IE, in this case you should use urlObj.search and handle the query parsing by yourself.
    } catch (e) {
        console.warn(`The URL '${url}' is not valid.`);
    }
}

Теперь в любом случае вызова скрипта, как в случае с OP:

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

В file.jsскрипте теперь вы можете сделать:

const params = getFileParams();
console.log(params.get('obj2'));
// Prints: someothervalue

Это также будет работать с RequireJS и другими динамически внедренными файловыми сценариями.

Славик Мельцер
источник