urlcodec: When encodeURIComponent Broke My Query String Builder
Debugging UTM links for a static newsletter. Built encode/decode with component vs full URI modes and a live query-string parser.
Problem: encodeURI left & intact in a value. encodeURIComponent over-encoded a full URL. Needed both modes in one static page.
Full tool
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>URL Encoder / Decoder</title>
<style>
body { font-family: system-ui; max-width: 760px; margin: 2rem auto; padding: 0 1rem; }
textarea, input[type=text] { width: 100%; font-family: monospace; }
textarea { min-height: 100px; }
.row { display: flex; gap: .5rem; flex-wrap: wrap; margin: .5rem 0; }
table { width: 100%; border-collapse: collapse; margin-top: 1rem; font-size: 14px; }
td, th { border: 1px solid #e2e8f0; padding: .4rem .6rem; text-align: left; }
</style>
</head>
<body>
<h1>URL Encoder / Decoder</h1>
<textarea id="in" placeholder="https://example.com?q=hello world"></textarea>
<div class="row">
<button id="enc-comp">encodeURIComponent</button>
<button id="enc-uri">encodeURI</button>
<button id="dec">decodeURIComponent</button>
<button id="parse">Parse query string</button>
</div>
<textarea id="out" readonly></textarea>
<table id="params" hidden><thead><tr><th>Key</th><th>Value</th></tr></thead><tbody></tbody></table>
<script>
const input = document.getElementById('in');
const output = document.getElementById('out');
const table = document.getElementById('params');
const tbody = table.querySelector('tbody');
document.getElementById('enc-comp').onclick = () => {
output.value = encodeURIComponent(input.value);
};
document.getElementById('enc-uri').onclick = () => {
output.value = encodeURI(input.value);
};
document.getElementById('dec').onclick = () => {
try {
output.value = decodeURIComponent(input.value.replace(/\+/g, ' '));
} catch (e) {
output.value = 'Error: ' + e.message;
}
};
document.getElementById('parse').onclick = () => {
tbody.innerHTML = '';
let qs = input.value;
if (qs.includes('?')) qs = qs.split('?')[1];
if (qs.includes('#')) qs = qs.split('#')[0];
const params = new URLSearchParams(qs);
if ([...params].length === 0) {
table.hidden = true;
return;
}
for (const [k, v] of params) {
const tr = document.createElement('tr');
tr.innerHTML = '<td>' + k + '</td><td>' + v + '</td>';
tbody.appendChild(tr);
}
table.hidden = false;
};
</script>
</body>
</html>
encodeURI vs encodeURIComponent
| Function | Encodes & in value? | Use for |
|---|---|---|
| encodeURI | No | Full URLs |
| encodeURIComponent | Yes | Query param values |
Plus-sign gotcha
Form posts use + for space. Decoder replaces + before decodeURIComponent.
Real bug
Double-encoding UTM params: %2520 instead of %20. Decoder shows garbage — I added a note to decode once only.
Verify
Input hello world → encodeURIComponent → hello%20world
Paste ?utm_source=newsletter&utm_campaign=march → parse table shows 2 rows