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.currentScript
is not available.
Also I do not want to rely on document.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>
content
is placed at the first array position:
meta[N][0]
in the example
data-M
gets additional tabular parameters put at the M+1
th position of the array:
metas[N][M+1]
in the example
N
is the N
th meta, where 0
is the first <meta>
data-X
where X
is 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.meta
is 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 throw
in 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.