Building a Clearable Input Field: HTML, JavaScript

Forms are the backbone of user interaction on websites. They allow users to submit information, search for data, and interact with your application. A key element of any form is the input field, where users enter their text, choose options, or upload files.

In this post, we’ll explore how to create input fields using JavaScript, giving you more control over your form’s functionality.

Step1: Input Field with Clear Button

This HTML structure represents an input field with a clear button. The input is enclosed within a div with a placeholder, while the button contains an SVG icon for clearing the input.

<div class="input">
<div class="text">
<input type="text" placeholder="Input Your Data" />
</div>
<button class="clear">
<svg viewBox="0 0 24 24">
<path class="line" d="M2 2L22 22" />
<path class="long" d="M9 15L20 4" />
<path class="arrow" d="M13 11V7" />
<path class="arrow" d="M17 11H13" />
</svg>
</button>
</div>

Step2: Styling with CSS Variables

This CSS code defines the styling for an input field with a clear button using custom properties and transitions. It includes dynamic styles for hover, focus, and clearing states, creating an interactive and visually appealing user interface.

.input {
--background: #bfa43b;
--border-default: #D0D0DF;
--border-active: #3D6DF9;
--shadow-default: #{rgba(#202048, .12)};
--shadow-active: #{rgba(#3D6DF9, .25)};
--text-color: white;
--placeholder-color: white;
--placeholder-color-hover: #BABAC9;
--close: #818190;
--close-light: #BABAC9;
--close-background: #F1F1FA;
width: 100%;
max-width: 240px;
display: flex;
align-items: center;
position: relative;
border-radius: 8px;
background: var(--background);
box-shadow: inset 0 0 0 var(--border-width, 1px) var(--border, var(--border-default)), 0 1px 3px var(--shadow, var(--shadow-default));
transition: box-shadow .2s;
--clear-x: 0px;
--clear-swipe-left: 0px;
--clear-swipe-x: 0;
--clear-swipe: 0px;
--clear-scale: 0;
--clear-rotate: 0deg;
--clear-opacity: 0;
--clear-arrow-o: 1;
--clear-arrow-x: 0px;
--clear-arrow-y: 0px;
--clear-arrow-offset: 4px;
--clear-arrow-offset-second: 4px;
--clear-line-array: 8.5px;
--clear-line-offset: 27px;
--clear-long-array: 8.5px;
--clear-long-offset: 24px;
&.clearing,
&:focus-within {
--border-width: 1.5px;
--border: var(--border-active);
--shadow: var(--shadow-active);
}
&.clearing {
--close-background: transparent;
--clear-arrow-stroke: var(--close-light);
}
.text {
flex-grow: 1;
input {
-webkit-appearance: none;
line-height: 24px;
background: none;
border: none;
outline: none;
display: block;
width: 100%;
margin: 0;
padding: 12px 16px;
font-family: inherit;
font-size: 14px;
font-weight: 500;
color: var(--text-color);
&::placeholder {
color: var(--placeholder-color);
transition: color .2s;
}
}
}
&:hover {
.text {
input {
&::placeholder {
color: var(--placeholder-color-hover);
}
}
}
}
.clear {
-webkit-appearance: none;
position: relative;
outline: none;
z-index: 1;
padding: 0;
margin: 12px 12px 12px 0;
border: none;
background: var(--b, transparent);
transition: background .2s;
border-radius: 50%;
opacity: var(--clear-opacity);
transform: scale(var(--clear-scale)) translateZ(0);
&:before {
content: '';
position: absolute;
top: 0;
bottom: 0;
right: 12px;
left: var(--clear-swipe-left);
background: var(--background);
transform-origin: 100% 50%;
transform: translateX(var(--clear-swipe)) scaleX(var(--clear-swipe-x)) translateZ(0);
}
svg {
display: block;
position: relative;
z-index: 1;
width: 24px;
height: 24px;
outline: none;
cursor: pointer;
fill: none;
stroke-width: 1.5;
stroke-linecap: round;
stroke-linejoin: round;
stroke: var(--close);
transform: translateX(var(--clear-x)) rotate(var(--clear-rotate)) translateZ(0);
path {
transition: stroke .2s;
&.arrow {
stroke: var(--clear-arrow-stroke, var(--close));
stroke-dasharray: 4px;
stroke-dashoffset: var(--clear-arrow-offset);
opacity: var(--clear-arrow-o);
transform: translate(var(--clear-arrow-x), var(--clear-arrow-y)) translateZ(0);
&:last-child {
stroke-dashoffset: var(--clear-arrow-offset-second);
}
}
&.line {
stroke-dasharray: var(--clear-line-array) 28.5px;
stroke-dashoffset: var(--clear-line-offset);
}
&.long {
stroke: var(--clear-arrow-stroke, var(--close));
stroke-dasharray: var(--clear-long-array) 15.5px;
stroke-dashoffset: var(--clear-long-offset);
opacity: var(--clear-arrow-o);
transform: translate(var(--clear-arrow-x), var(--clear-arrow-y)) translateZ(0);
}
}
}
&:hover {
--b: var(--close-background);
}
}
}

html {
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
}

* {
box-sizing: inherit;
&:before,
&:after {
box-sizing: inherit;
}
}

// Center & dribbble
body {
min-height: 100vh;
display: flex;
font-family: 'Inter', Arial;
justify-content: center;
align-items: center;
background: #fff;
.dribbble {
position: fixed;
display: block;
right: 20px;
bottom: 20px;
img {
display: block;
height: 28px;
}
}
.twitter {
position: fixed;
display: block;
right: 64px;
bottom: 14px;
svg {
width: 32px;
height: 32px;
fill: #1da1f2;
}
}
}

Step3: JavaScript Animation

This code implements the clear button functionality for the input field. It uses JavaScript animation libraries (gsap) to create a dynamic clearing animation with SVG manipulation for the clear icon.

const { to, set, timeline } = gsap

function delay(fn, ms) {
let timer = 0
return function(...args) {
clearTimeout(timer)
timer = setTimeout(fn.bind(this, ...args), ms || 0)
}
}

document.querySelectorAll('.input').forEach(elem => {
let clear = elem.querySelector('.clear'),
input = elem.querySelector('input'),
{ classList } = elem,
svgLine = clear.querySelector('.line'),
svgLineProxy = new Proxy({
x: null
}, {
set(target, key, value) {
target[key] = value
if(target.x !== null) {
svgLine.setAttribute('d', getPath(target.x, .1925))
}
return true
},
get(target, key) {
return target[key]
}
})

svgLineProxy.x = 0

input.addEventListener('input', delay(e => {
let bool = input.value.length
to(elem, {
'--clear-scale': bool ? 1 : 0,
duration: bool ? .5 : .15,
ease: bool ? 'elastic.out(1, .7)' : 'none'
})
to(elem, {
'--clear-opacity': bool ? 1 : 0,
duration: .15
})
}, 250))

clear.addEventListener('click', e => {
classList.add('clearing')
set(elem, {
'--clear-swipe-left': (input.offsetWidth - 16) * -1 + 'px'
})
to(elem, {
keyframes: [{
'--clear-rotate': '45deg',
duration: .25
}, {
'--clear-arrow-x': '2px',
'--clear-arrow-y': '-2px',
duration: .15
}, {
'--clear-arrow-x': '-3px',
'--clear-arrow-y': '3px',
'--clear-swipe': '-3px',
duration: .15,
onStart() {
to(svgLineProxy, {
x: 3,
duration: .1,
delay: .05
})
}
}, {
'--clear-swipe-x': 1,
'--clear-x': (input.offsetWidth) * -1 + 'px',
duration: .45,
onComplete() {
input.value = ''
input.focus()
to(elem, {
'--clear-arrow-offset': '4px',
'--clear-arrow-offset-second': '4px',
'--clear-line-array': '8.5px',
'--clear-line-offset': '27px',
'--clear-long-offset': '24px',
'--clear-rotate': '0deg',
'--clear-arrow-o': 1,
duration: 0,
delay: .7,
onStart() {
classList.remove('clearing')
}
})
to(elem, {
'--clear-opacity': 0,
duration: .2,
delay: .55
})
to(elem, {
'--clear-arrow-o': 0,
'--clear-arrow-x': '0px',
'--clear-arrow-y': '0px',
'--clear-swipe': '0px',
duration: .15
})
to(svgLineProxy, {
x: 0,
duration: .45,
ease: 'elastic.out(1, .75)'
})
}
}, {
'--clear-swipe-x': 0,
'--clear-x': '0px',
duration: .4,
delay: .35
}]
})
to(elem, {
'--clear-arrow-offset': '0px',
'--clear-arrow-offset-second': '8px',
'--clear-line-array': '28.5px',
'--clear-line-offset': '57px',
'--clear-long-offset': '17px',
duration: .2
})
})
})

function getPoint(point, i, a, smoothing) {
let cp = (current, previous, next, reverse) => {
let p = previous || current,
n = next || current,
o = {
length: Math.sqrt(Math.pow(n[0] - p[0], 2) + Math.pow(n[1] - p[1], 2)),
angle: Math.atan2(n[1] - p[1], n[0] - p[0])
},
angle = o.angle + (reverse ? Math.PI : 0),
length = o.length * smoothing;
return [current[0] + Math.cos(angle) * length, current[1] + Math.sin(angle) * length];
},
cps = cp(a[i - 1], a[i - 2], point, false),
cpe = cp(point, a[i - 1], a[i + 1], true);
return `C ${cps[0]},${cps[1]} ${cpe[0]},${cpe[1]} ${point[0]},${point[1]}`;
}

function getPath(x, smoothing) {
return [
[2, 2],
[12 - x, 12 + x],
[22, 22]
].reduce((acc, point, i, a) => i === 0 ? `M ${point[0]},${point[1]}` : `${acc} ${getPoint(point, i, a, smoothing)}`, '')
}

Building a Clearable Input Field: HTML, JavaScript Demo

Creating input fields in JavaScript is a fundamental skill for web developers. This simple guide walks you through the process, from setting up the HTML structure to writing the JavaScript code.

You Might Be Interested In:

Ashfaq Ahmed is a freelance WordPress developer and owner of codeconvey.com. I developed modern and highly interactive websites. I always focus on improving my development and design skills.

Leave a Comment