I read the tutorial DIY widgets - How to embed your site on another site for XSS Widgets by Dr. Nic.
I'm looking for a way to pass parameters to the script tag. For example, to make the following work:
<script src="http://path/to/widget.js?param_a=1&param_b=3"></script>
Is there a way to do this?
Two interesting links:
-
See also: stackoverflow.com/q/1017424/247696Flimm– Flimm2015年06月30日 15:50:16 +00:00Commented Jun 30, 2015 at 15:50
16 Answers 16
I apologise for replying to a super old question but after spending an hour wrestling with the above solutions I opted for simpler stuff.
<script src=".." one="1" two="2"></script>
Inside above script:
document.currentScript.getAttribute('one'); // 1
document.currentScript.getAttribute('two'); // 2
Much easier than jQuery or URL parsing.
You might need the polyfill for document.currentScript from @Yared Rodriguez's answer for IE:
document.currentScript = document.currentScript || (function() {
var scripts = document.getElementsByTagName('script');
return scripts[scripts.length - 1];
})();
12 Comments
src="..." This is meant to signify, ANY src, doesn't matter :)It's better to Use feature in html5 5 data Attributes
<script src="http://path.to/widget.js" data-width="200" data-height="200">
</script>
Inside the script file http://path.to/widget.js you can get the paremeters in that way:
<script>
function getSyncScriptParams() {
var scripts = document.getElementsByTagName('script');
var lastScript = scripts[scripts.length-1];
var scriptName = lastScript;
return {
width : scriptName.getAttribute('data-width'),
height : scriptName.getAttribute('data-height')
};
}
</script>
6 Comments
myAwesomeWigretNo2, or myAwesomeWigretNo681 ??? How can I fix this to accept a real variable?document.currentScript (only works for scripts not in their own file) and putting an id on the <script> tag and then finding it by that.Another way is to use meta tags. Whatever data is supposed to be passed to your JavaScript can be assigned like this:
<meta name="yourdata" content="whatever" />
<meta name="moredata" content="more of this" />
The data can then be pulled from the meta tags like this (best done in a DOMContentLoaded event handler):
var data1 = document.getElementsByName('yourdata')[0].content;
var data2 = document.getElementsByName('moredata')[0].content;
Absolutely no hassle with jQuery or the likes, no hacks and workarounds necessary, and works with any HTML version that supports meta tags...
3 Comments
<script type="module"> with a <meta name> derived from import.meta.url. document.currentScript is not available in modules. document.body.getElementsByTagName('script').pop() cannot guarantee to access the current module in an asynchronous world either. import.meta.url is absolute while <script src> may be anything. Hardcoding some ID might conflict with other modules using the same technique.Got it. Kind of a hack, but it works pretty nice:
var params = document.body.getElementsByTagName('script');
query = params[0].classList;
var param_a = query[0];
var param_b = query[1];
var param_c = query[2];
I pass the params in the script tag as classes:
<script src="http://path.to/widget.js" class="2 5 4"></script>
This article helped a lot.
3 Comments
JQuery has a way to pass parameters from HTML to javascript:
Put this in the myhtml.html file:
<!-- Import javascript -->
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
<!-- Invoke a different javascript file called subscript.js -->
<script id="myscript" src="subscript.js" video_filename="foobar.mp4">/script>
In the same directory make a subscript.js file and put this in there:
//Use jquery to look up the tag with the id of 'myscript' above. Get
//the attribute called video_filename, stuff it into variable filename.
var filename = $('#myscript').attr("video_filename");
//print filename out to screen.
document.write(filename);
Analyze Result:
Loading the myhtml.html page has 'foobar.mp4' print to screen. The variable called video_filename was passed from html to javascript. Javascript printed it to screen, and it appeared as embedded into the html in the parent.
jsfiddle proof that the above works:
Comments
Create an attribute that contains a list of the parameters, like so:
<script src="http://path/to/widget.js" data-params="1, 3"></script>
Then, in your JavaScript, get the parameters as an array:
var script = document.currentScript ||
/*Polyfill*/ Array.prototype.slice.call(document.getElementsByTagName('script')).pop();
var params = (script.getAttribute('data-params') || '').split(/, */);
params[0]; // -> 1
params[1]; // -> 3
2 Comments
If you are using jquery you might want to consider their data method.
I have used something similar to what you are trying in your response but like this:
<script src="http://path.to/widget.js" param_a = "2" param_b = "5" param_c = "4">
</script>
You could also create a function that lets you grab the GET params directly (this is what I frequently use):
function $_GET(q,s) {
s = s || window.location.search;
var re = new RegExp('&'+q+'=([^&]*)','i');
return (s=s.replace(/^\?/,'&').match(re)) ? s=s[1] : s='';
}
// Grab the GET param
var param_a = $_GET('param_a');
Comments
it is a very old thread, I know but this might help too if somebody gets here once they search for a solution.
Basically I used the document.currentScript to get the element from where my code is running and I filter using the name of the variable I am looking for. I did it extending currentScript with a method called "get", so we will be able to fetch the value inside that script by using:
document.currentScript.get('get_variable_name');
In this way we can use standard URI to retrieve the variables without adding special attributes.
This is the final code
document.currentScript.get = function(variable) {
if(variable=(new RegExp('[?&]'+encodeURIComponent(variable)+'=([^&]*)')).exec(this.src))
return decodeURIComponent(variable[1]);
};
I was forgetting about IE :) It could not be that easier... Well I did not mention that document.currentScript is a HTML5 property. It has not been included for different versions of IE (I tested until IE11, and it was not there yet). For IE compatibility, I added this portion to the code:
document.currentScript = document.currentScript || (function() {
var scripts = document.getElementsByTagName('script');
return scripts[scripts.length - 1];
})();
What we are doing here is to define some alternative code for IE, which returns the current script object, which is required in the solution to extract parameters from the src property. This is not the perfect solution for IE since there are some limitations; If the script is loaded asynchronously. Newer browsers should include ".currentScript" property.
I hope it helps.
Comments
Thanks to the jQuery, a simple HTML5 compliant solution is to create an extra HTML tag, like div, to store the data.
HTML:
<div id='dataDiv' data-arg1='content1' data-arg2='content2'>
<button id='clickButton'>Click me</button>
</div>
JavaScript:
$(document).ready(function() {
var fetchData = $("#dataDiv").data('arg1') +
$("#dataDiv").data('arg2') ;
$('#clickButton').click(function() {
console.log(fetchData);
})
});
Live demo with the code above: http://codepen.io/anon/pen/KzzNmQ?editors=1011#0
On the live demo, one can see the data from HTML5 data-* attributes to be concatenated and printed to the log.
Source: https://api.jquery.com/data/
2 Comments
This is the Solution for jQuery 3.4
<script src="./js/util.js" data-m="myParam"></script>
$(document).ready(function () {
var m = $('script[data-m][data-m!=null]').attr('data-m');
})
1 Comment
I wanted solutions with as much support of old browsers as possible. Otherwise I'd say either the currentScript or the data attributes method would be most stylish.
This is the only of these methods not brought up here yet. Particularly, if for some reason you have great amounts of data, then the best option might be:
localStorage
/* On the original page, you add an inline JS Script.
* If you only have one datum you don't need JSON:
* localStorage.setItem('datum', 'Information here.');
* But for many parameters, JSON makes things easier: */
var data = {'data1': 'I got a lot of data.',
'data2': 'More of my data.',
'data3': 'Even more data.'};
localStorage.setItem('data', JSON.stringify(data));
/* External target JS Script, where your data is needed: */
var data = JSON.parse(localStorage.getItem('data'));
console.log(data['data1']);
localStorage has full modern browser support, and surprisingly good support of older browsers too, back to IE 8, Firefox 3,5 and Safari 4 [eleven years back] among others.
If you don't have a lot of data, but still want extensive browser support, maybe the best option is:
Meta tags [by Robidu]
/* HTML: */
<meta name="yourData" content="Your data is here" />
/* JS: */
var data1 = document.getElementsByName('yourData')[0].content;
The flaw of this, is that the correct place to put meta tags [up until HTML 4] is in the head tag, and you might not want this data up there. To avoid that, or putting meta tags in body, you could use a:
Hidden paragraph
/* HTML: */
<p hidden id="yourData">Your data is here</p>
/* JS: */
var yourData = document.getElementById('yourData').innerHTML;
For even more browser support, you could use a CSS class instead of the hidden attribute:
/* CSS: */
.hidden {
display: none;
}
/* HTML: */
<p class="hidden" id="yourData">Your data is here</p>
3 Comments
Put the values you need someplace where the other script can retrieve them, like a hidden input, and then pull those values from their container when you initialize your new script. You could even put all your params as a JSON string into one hidden field.
2 Comments
It's simpler if you pass arguments without names, just like function calls.
In HTML:
<script src="abc.js" data-args="a,b"></script>
Then, in JavaScript:
const args=document.currentScript.dataset.args.split(',');
Now args contains the array ['a','b']. This assumes synchronous script calling.
1 Comment
dcoument.readyState is set to complete, it doesn't point anywhere. In contrast to this meta tags don't make your JS dependent on being executed synchronously (thus works with both defer and async attributes) and can be read from virtually every JS incorporated into your page. If you want to achieve the same with data attributes, you have quite a rigmarole ahead of you to even retrieve the data.Parameters for <script type="module">
I had the problem to pass parameters to some JS script loaded as a module.
There are two variants, based on the ideas of @Robidy and @Fom.
In modules,
document.currentScriptis not available. Also I do not want to rely ondocument.scripts[document.scripts.length-1]when it comes to possibly asynchronously executing modules.
@Robidy: parameters from <meta name="script.js" content>
While using <meta> for this seems clumsy on the first sight, it starts to have some benefits:
- You can also pass (possibly sparsely filled) tabular data.
- The name can be derived from the module's name.
<meta name="module.js" content="https://cdn.skypack.dev/octokit" data-0="https://cdn.skypack.dev/[email protected]"/>
<script type="module" src="module.js"></script>
contentis placed at the first array position:meta[N][0]in the example
data-Mgets additional tabular parameters put at theM+1th position of the array:metas[N][M+1]in the example
Nis theNth meta, where0is the first<meta>data-XwhereXis not a positive integer, it is ignored
I needed this, as the documented way to load Octokit failed. So I created a loader module which allows to add fallback URLs.
(example use)It does not support SRI, though.
Here is the beginning of the code of such a module.js:
const me = import.meta.url?.split('/').pop() || 'module.js';
const metas = Array
.from(document.getElementsByName(me))
.filter(_ => _.nodeName === 'META')
.map(_ => [_.attributes.content.value].concat(Array.from(_.attributes)
.map(_ => ([_.name.split('-'), _.value]))
.filter(([name,value]) => name.length===2 && name[0] ==='data' && name[1] === `${name[1]||0}`)
.reduce((a,[[data,nr],value]) => (a[nr|0]=value, a), [])
));
I have no idea what happens if
import.metais not available.For the case this is no syntax error, above code uses some hardcoded value as fallback (here
'module.js'), named after the default name of a module of course.Perhaps a better thing would be to
throwin this case.
The variable metas (from the example) then is an array of possibly sparsely filled arrays with the <meta> rows' content, data-0, data-1, .. and so on.
<meta name="module.js" content="https://cdn.skypack.dev/octokit" data-0="https://cdn.skypack.dev/[email protected]"/>
gives
[['https://cdn.skypack.dev/octokit','https://cdn.skypack.dev/[email protected]']]
and
<meta name="module.js" content="https://cdn.skypack.dev/octokit" data-2="https://cdn.skypack.dev/[email protected]"/>
gives (note the tripple comma):
[['https://cdn.skypack.dev/octokit',,,'https://cdn.skypack.dev/[email protected]']]
@Fom: JSON from hidden <div>
To parse hidden <div> for JSON, as suggested by @Fom, might look like:
const me = import.meta.url?.split('/').pop() || 'module.js';
const jsons = Array
.from(document.getElementsByName(me))
.filter(_ => _.nodeName === 'DIV')
.map(_ => JSON.parse(_.innerText));
This hands out an array of the JSONs found in all <div name> elements named after the module. (You might need to wait until all DOM is loaded.)
The automatically derived name:
If you use something like
<script type="module" src="/path/to/module.js#abc"></script>
the line
const me = import.meta.url?.split('/').pop();
sets me to module.js#abc, so you need to give name like following:
<meta name="module.js#abc" content="parameter"/>
<div name="module.js#abc">{"a":"b"}</div>
<script type="module" src="/path/to/module.js#abc"></script>
Note that JS apparently allows arbitrary strings in name.
Final note
This all works similar for normal <script> without <script type="module">, if you replace import.meta.url with document?.currentScript.src
To change the code such, that it works for normal scripts, modules and WebWorkers, and create some meaningful script which can be executed as any of these, is left as an exercise for the reader.
1 Comment
document.getElementByTagName(), then iterate over the list), then return some sort of API for easy access. If done right, you can even access comma(or whatever)-separated lists into the meta tags and check them for individual items without further ado.data attributes
If you combine all the information here you arrive at a best practice answer. Note that this way everything will be a string.
<script src="index.js" data-foo="bar" data-foo-bar="1"></script>
index.js
// Can also destructure here if you prefer
const args = document.currentScript.dataset;
// args.foo is "bar", args.fooBar is "1"
URL params
Should you for some reason prefer to use URL params, here is what I got for that. It has some syntax disadvantages. Also, unfortunately everything is still a string.
<script src="index.js?foo=bar&foo-bar=1"></script>
index.js
const args = Object.fromEntries(new URL(document.currentScript.src).searchParams);
// args.foo is "bar", args.foo-bar is "1"
See Also
Comments
If script is type="module", it will know its URL from import.meta.url. You can pass search params.
Comments
Explore related questions
See similar questions with these tags.