Passing options through script tags

Use script tag attributes to pass config values.

When I need to pass config options to an external JavaScript code included using <script> tag I have several options. The simplest way is to create a global config object

page
1
2
3
4
5
6
<script>
var fooConfig = {
bar: 'baz'
};
</script>
<script src="foo.js"></script>
foo.js
1
2
3
4
// foo.js
if (typeof fooConfig !== 'undefined') {
...
}

I do not like this approach, unless there is a large and complicated configuration object. This approach pollutes the global environment, even when I delete the fooConfig inside foo.js after I am done reading the options.

My second approach is to register a function that accepts the settings first, and then call it with the actual config object

1
2
3
4
<script src="foo.js"></script>
foo({
bar: 'baz'
});

This approach separates the configuration from the execution step, but pollutes the global environment, and might not work correctly with scripts loaded using async flag.

The third approach I like is inspired by AngularJs widgets.

<my-widget-foo bar="baz"></my-widget-foo>

I can pass the config values as script tag's attributes

<script src="foo.js" bar="baz" name="foo"></script>  

The attribute name="foo" is necessary to make sure we grab the correct options, even if the script tag is loaded asynchronously. I prefer using the attribute values to the query arguments (src="foo?bar=baz&...) for clarity.

Inside the script we can grab attributes for a tags with the given name. Because getElementsByTagName returns NodeList we need to apply Array.prototype.some in order to conveniently iterate over the list.

1
2
3
4
5
6
7
8
9
10
11
function getScriptAttributes(name) {
var scriptEls = document.getElementsByTagName('script');
var found;
Array.prototype.some.call(scriptEls, function (script) {
if (script.attributes.name && script.attributes.name.value === name) {
found = namedNodeMapToObject(script.attributes);
return true;
}
});
return found;
}

The namedNodeMapToObject is a utility function that converts from a NamedNodeMap structure to a plain object

1
2
3
4
5
6
7
function namedNodeMapToObject(map) {
var result = {};
Array.prototype.forEach.call(map, function (attr) {
result[attr.name] = attr.value;
});
return result;
}

Finally, I placed the above code into script-attributes repository. You can include a small script and get the arguments like this

1
2
<script src="script-attributes.js"></script>
<script src="foo.js" name="foo" bar="baz"></script>
foo.js
1
var options = scriptAttributes('foo');