Read and write URLs in JavaScript
Recently I had create a form where users could supply a URL and then append utm tags for tracking in their Analytics package. I also wanted to show the final, tagged URL to the user.
Here is what the form looked like:
<form id="form">
<label for="url">URL</label>
<input type="url" id="url" name="url">
<label for="utm_source">UTM Source</label>
<input type="text" id="utm_source" name="utm_source">
<label for="utm_medium">UTM Medium</label>
<input type="text" id="utm_medium" name="utm_medium">
</form>
<p>Your final URL will be <span id="preview"></span></p>
You might be tempted to just concatenate the final url like so:
const url = document.getElementById('url').value;
const utmSource = document.getElementById('utm_source').value;
const utmMedium = document.getElementById('utm_medium').value;
document.getElementByid('preview').innerHtml = `${url}?utm_source=${utmSource}&utm_medium=${utmMedium}`;
Simples!
Not so fast: what if the url
provided already contains query parameters? You'd have two ?
in that URL. Sure, that's easy enough to solve with .includes('?')
.
But, what if the input string already contains a utm_source
or utm_medium
parameter? You'd need some regular expression to detect and handle this and prevent duplication.
Oh, and don't forget to url encode your query parameters.
Thankfully, there is a better way to do this in 2023: The URL
API has been around for a while, and according to caniuse.com support is good.
So, here is a much better way of dealing with this:
const url = new URL(document.getElementById('url').value);
url.searchParams.set('utm_source', document.getElementById('utm_source').value);
url.searchParams.set('utm_medium', document.getElementById('utm_medium').value);
document.getElementById('preview').innerHTML = url.toString();
Any existing query parameters in the provided URL will simply be retained.
Your query parameters will be encoded automatically too.
If the URL contains a utm_source
or utm_medium
parameter, it'll be overridden to prevent duplication, which is what I wanted here.
If you wanted to keep the parameters from the input url instead, you could just check like this:
if(!url.searchParams.has('utm_source')) {
url.searchParams.set('utm_source', document.getElementById('utm_source').value);
}
Or, if you wanted to allow duplicates you could append values:
url.searchParams.append('utm_source', document.getElementById('utm_source').value);
This is far neater and less error prone than our first attempt.
Gotchas
One thing to look out for, is that new URL(value)
will throw an error, if value
isn't a valid URL, including for relative URLs. URLs without a protocol (such as www.example.com/foo/bar
) will be considered invalid URLs, so you'll definitely want some error handling here. (You can provide a base URL as second parameter, but that's not useful for my use case.)
url.searchParams
is of type URLSearchParams
which has a few more useful methods.