JSFiddle - React, Tailwind, and code Playground
by Fabio Dan
HTML
<main>
<div class="postcode-field">
<label class="postcode-field__label">Postcode</label>
<input placeholder="Sector or unit" class="postcode-field__input" type="text" maxlength="8" pattern="[a-zA-Z]{1,2}[0-9][0-9a-zA-Z]?\s?[0-9]|[a-zA-Z]{1,2}[0-9][0-9a-zA-Z]?\s?[0-9][a-zA-Z]{2}"><br>
<div class="postcode-field__message">
Sector or unit invalid
</div>
<div class="postcode-field__coverage">
Coverage: <span class="postcode-field__coverage__value"></span>
</div>
</div>
<div class="postcode-samples">
<h4 class="postcode-samples__heading">Postcode variations</h4>
<ul>
<li>EC1A 1BB</li>
<li>W1A 0AX</li>
<li>M1 1AE</li>
<li>B33 8TH</li>
<li>CR2 6XH</li>
<li>DN55 1PT</li>
</ul>
</div>
</main>
SCSS
html {
box-sizing: border-box;
height: 100%;
}
*, *::before, *::after {
box-sizing: inherit;
}
body {
font-family: "Roboto", "Helvetica Neue", sans-serif;
color: #3d3d3d;
display: flex;
margin-top: 10%;
justify-content: center;
height: 100%;
}
main {
display: inline-block;
}
.postcode-field {
width: 255px;
position: relative;
}
.postcode-field__message,
.postcode-field__coverage,
.postcode-field__label {
display: block;
font-size: 12px;
color: #666666;
}
.postcode-field__label {
padding-bottom: 5px;
}
.postcode-field__label:after {
content: " *";
color: #CC0000;
}
.postcode-field__input {
width: 100%;
height: 34px;
border: solid 1px #ccc;
color: #3d3d3d;
border-radius: 4px;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
padding: 0 12px;
font-size: 14px;
text-transform: uppercase;
&::placeholder {
color: #A9A9A9;
text-transform: none;
}
}
.postcode-field--validation:not(:focus):invalid {
border-color: #CC0000;
& ~ .postcode-field__message {
display: block;
}
& ~ .postcode-field__coverage {
display: none;
}
}
.postcode-field__message,
.postcode-field__coverage {
position: absolute;
left: 0;
bottom: -1;
}
.postcode-field__message {
color: #CC0000;
display: none;
}
.postcode-field__coverage {
left: auto;
right: 0;
}
.postcode-samples {
margin-top: 80px;
font-size: 14px;
}
.postcode-samples__heading {
font-weight: 500;
}
JavaScript
const inputElem = document.querySelector('.postcode-field__input')
const coverageElem = document.querySelector('.postcode-field__coverage__value')
const regex = {
whiteSpace: /\s/g,
sector: /[a-zA-Z]{1,2}[0-9][0-9a-zA-Z]?\s?[0-9]/g,
unit: /[a-zA-Z]{1,2}[0-9][0-9a-zA-Z]?\s?[0-9][a-zA-Z]{2}/g,
}
function formatPostCode(value) {
const formattedValue = value.replace(/ /g, '').replace(/^(.*)(\d)/, "$1 $2")
inputElem.value = formattedValue
}
function onPostCodeInput(e) {
const value = e.target.value;
if (value.match(regex.unit)) {
setCoverage('Unit')
formatPostCode(value)
} else if (value.match(regex.sector)) {
setCoverage('Sector')
formatPostCode(value)
} else {
setCoverage('Not defined')
}
}
function setCoverage(value) {
coverageElem.innerHTML = value
}
function validate(e) {
const value = e.target.value
const isValid = regex.unit.test(value) || regex.unit.test(value)
if (isValid) {
inputElem.classList.remove('postcode-field--validation')
} else {
inputElem.classList.add('postcode-field--validation')
}
return isValid
}
function init() {
setCoverage('Not defined')
inputElem.addEventListener("input", onPostCodeInput)
inputElem.addEventListener('blur', validate)
}
init();