01. Click
The most common interaction. Fires once when the user releases the mouse button.
Preview index.html style.css script.js
< button class = "btn" id = "click-btn" >Click me</ button >
< div class = "output" id = "click-out" >No clicks yet</ div > .btn {
padding : .75 rem 1.5 rem ;
background : #6366f1 ;
color : white ;
border : none ;
border-radius : 8 px ;
font-size : 1 rem ;
cursor : pointer ;
}
.output {
margin-top : 1 rem ;
font-family : monospace ;
font-size : .875 rem ;
color : #64748b ;
} setTimeout (() => {
let count = 0 ;
const btn = document. getElementById ( "click-btn" );
const out = document. getElementById ( "click-out" );
if ( ! btn || ! out) return ;
btn. addEventListener ( "click" , () => {
count ++ ;
out.textContent = `Clicked ${ count } time${ count > 1 ? "s" : ""}` ;
});
}, 0 );
const btn = document. querySelector ( "#click-btn" )
btn. addEventListener ( "click" , () => {
// runs every time the user clicks
})
02. Double Click
dblclick fires only on two rapid clicks. Useful for toggles or zooming.
Preview index.html style.css script.js
< div class = "dbl-target" id = "dbl-box" >Double-click me</ div >
< div class = "out" id = "dbl-out" >Waiting for dblclick...</ div > .dbl-target {
width : 160 px ;
height : 80 px ;
background : #6366f1 ;
border-radius : 12 px ;
display : flex ;
align-items : center ;
justify-content : center ;
color : white ;
font-weight : 600 ;
cursor : pointer ;
user-select : none ;
transition : transform .15 s ;
}
.dbl-target.zoomed {
transform : scale ( 1.3 );
background : #4f46e5 ;
}
.out {
margin-top : .75 rem ;
font-family : monospace ;
font-size : .875 rem ;
color : #64748b ;
} setTimeout (() => {
const box = document. getElementById ( "dbl-box" );
const out = document. getElementById ( "dbl-out" );
if ( ! box || ! out) return ;
box. addEventListener ( "dblclick" , () => {
box.classList. toggle ( "zoomed" );
out.textContent = `dblclick fired - zoomed: ${ box . classList . contains ( "zoomed" ) }` ;
});
}, 0 );
btn. addEventListener ( "dblclick" , () => {
// runs only on two rapid clicks
})
03. Mouse Move
Fires continuously as the cursor moves. e.offsetX/e.offsetY give position relative to the element.
Preview index.html style.css script.js
< div class = "zone" id = "mouse-zone" >
Move your mouse here
< div class = "dot" id = "dot" ></ div >
</ div >
< div class = "output" id = "mouse-out" >x: - , y: -</ div > .zone {
width : 100 % ;
max-width : 360 px ;
height : 160 px ;
background : #f1f5f9 ;
border : 2 px dashed #cbd5e1 ;
border-radius : 12 px ;
display : flex ;
align-items : center ;
justify-content : center ;
font-size : .875 rem ;
color : #94a3b8 ;
cursor : crosshair ;
position : relative ;
}
.dot {
position : absolute ;
width : 10 px ;
height : 10 px ;
background : #6366f1 ;
border-radius : 50 % ;
transform : translate ( -50 % , -50 % );
pointer-events : none ;
display : none ;
}
.output {
margin-top : .75 rem ;
font-family : monospace ;
font-size : .875 rem ;
color : #64748b ;
} setTimeout (() => {
const zone = document. getElementById ( "mouse-zone" );
const dot = document. getElementById ( "dot" );
const out = document. getElementById ( "mouse-out" );
if ( ! zone || ! dot || ! out) return ;
zone. addEventListener ( "mousemove" , ( e ) => {
const rect = zone. getBoundingClientRect ();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
dot.style.display = "block" ;
dot.style.left = x + "px" ;
dot.style.top = y + "px" ;
out.textContent = `x: ${ Math . round ( x ) } , y: ${ Math . round ( y ) }` ;
});
zone. addEventListener ( "mouseleave" , () => {
dot.style.display = "none" ;
});
}, 0 );
area. addEventListener ( "mousemove" , ( e ) => {
const x = e.offsetX
const y = e.offsetY
const hue = Math. round ((x / area.offsetWidth) * 360 )
area.style.background = `hsl(${ hue }, 65%, 50%)`
})
04. Keyboard
keydown fires on press, keyup on release. e.key gives you the character name.
Preview index.html style.css script.js
< input class = "input" id = "key-input" placeholder = "Type something..." />
< div class = "output" id = "key-out" >Last key: -</ div > .input {
padding : .6 rem 1 rem ;
border : 2 px solid #e2e8f0 ;
border-radius : 8 px ;
font-size : 1 rem ;
width : 100 % ;
max-width : 320 px ;
outline : none ;
transition : border-color .15 s ;
}
.input:focus {
border-color : #6366f1 ;
}
.output {
margin-top : 1 rem ;
font-family : monospace ;
font-size : .875 rem ;
color : #64748b ;
} setTimeout (() => {
const input = document. getElementById ( "key-input" );
const out = document. getElementById ( "key-out" );
if ( ! input || ! out) return ;
input. addEventListener ( "keydown" , ( e ) => {
out.textContent = `Last key: "${ e . key }" (code: ${ e . code })` ;
});
}, 0 );
document. addEventListener ( "keydown" , ( e ) => {
console. log (e.key) // "a", "Enter", "ArrowUp", " " (space)…
display.classList. add ( "active" )
})
document. addEventListener ( "keyup" , () => {
display.classList. remove ( "active" )
})
05. Drag & Drop
Add draggable="true" to any element. You must call e.preventDefault() in dragover or drop will never fire.
Preview index.html style.css script.js
< div class = "dnd-wrap" >
< div class = "dnd-zone" id = "dnd-a" >
< div class = "dnd-item" draggable = "true" id = "dnd-item" >Drag me - > </ div >
</ div >
< div class = "dnd-zone" id = "dnd-b" ></ div >
</ div >
< div class = "out" id = "dnd-out" >Drag the item between the two zones</ div > .dnd-wrap {
display : flex ;
gap : 1 rem ;
align-items : flex-start ;
}
.dnd-zone {
flex : 1 ;
min-height : 80 px ;
background : #f1f5f9 ;
border : 2 px dashed #cbd5e1 ;
border-radius : 8 px ;
padding : .5 rem ;
transition : border-color .15 s , background .15 s ;
}
.dnd-zone.over {
border-color : #6366f1 ;
background : #ede9fe ;
}
.dnd-item {
padding : .5 rem 1 rem ;
background : #6366f1 ;
color : white ;
border-radius : 6 px ;
cursor : grab ;
font-size : .875 rem ;
font-weight : 600 ;
display : inline-block ;
}
.dnd-item:active {
cursor : grabbing ;
}
.out {
margin-top : .75 rem ;
font-family : monospace ;
font-size : .8 rem ;
color : #64748b ;
} setTimeout (() => {
const item = document. getElementById ( "dnd-item" );
const zoneA = document. getElementById ( "dnd-a" );
const zoneB = document. getElementById ( "dnd-b" );
const out = document. getElementById ( "dnd-out" );
if ( ! item || ! zoneA || ! zoneB || ! out) return ;
item. addEventListener ( "dragstart" , ( e ) => {
e.dataTransfer. setData ( "text/plain" , "dnd-item" );
});
[zoneA, zoneB]. forEach (( zone ) => {
zone. addEventListener ( "dragover" , ( e ) => {
e. preventDefault ();
zone.classList. add ( "over" );
});
zone. addEventListener ( "dragleave" , () => zone.classList. remove ( "over" ));
zone. addEventListener ( "drop" , ( e ) => {
e. preventDefault ();
zone.classList. remove ( "over" );
zone. appendChild (item);
out.textContent = `Dropped in ${ zone . id === "dnd-a" ? "Zone A" : "Zone B"}` ;
});
});
}, 0 );
item. addEventListener ( "dragstart" , ( e ) => {
e.dataTransfer. setData ( "text/plain" , item.id)
})
target. addEventListener ( "dragover" , ( e ) => {
e. preventDefault () // required — without this, drop never fires
})
target. addEventListener ( "drop" , ( e ) => {
e. preventDefault ()
const id = e.dataTransfer. getData ( "text/plain" )
target. appendChild (document. getElementById (id))
})
06. Window Resize
Fires when the browser window changes size. Useful for responsive canvas or layout recalculations.
Preview index.html style.css script.js
< div class = "wr-display" id = "wr-size" >- × -</ div >
< div class = "wr-label" >window.innerWidth × window.innerHeight (resize the panel)</ div >
< div class = "wr-bar-wrap" >< div class = "wr-bar" id = "wr-bar" style = "width:50%" ></ div ></ div > .wr-display {
font-family : monospace ;
font-size : 2 rem ;
font-weight : 700 ;
color : #6366f1 ;
}
.wr-label {
font-size : .8 rem ;
color : #94a3b8 ;
margin-top : .25 rem ;
}
.wr-bar-wrap {
margin-top : .75 rem ;
height : 8 px ;
background : #e2e8f0 ;
border-radius : 4 px ;
}
.wr-bar {
height : 100 % ;
background : #6366f1 ;
border-radius : 4 px ;
transition : width .3 s ;
} setTimeout (() => {
const sizeEl = document. getElementById ( "wr-size" );
const barEl = document. getElementById ( "wr-bar" );
if ( ! sizeEl || ! barEl) return ;
const update = () => {
const w = window.innerWidth;
const h = window.innerHeight;
sizeEl.textContent = `${ w } × ${ h }` ;
barEl.style.width = Math. min ( 100 , (w / 1440 ) * 100 ) + "%" ;
};
window. addEventListener ( "resize" , update);
update ();
}, 0 );
window. addEventListener ( "resize" , () => {
console. log (window.innerWidth, window.innerHeight)
})
07. Camera
navigator.mediaDevices.getUserMedia() requests webcam access. Requires HTTPS or localhost.
Preview index.html style.css script.js
< video id = "cam-video" autoplay playsinline muted ></ video >
< div class = "cam-status" id = "cam-status" >Camera not started</ div >
< div class = "btns" >
< button class = "btn btn-start" id = "cam-start" >Request camera</ button >
< button class = "btn btn-stop" id = "cam-stop" >Stop</ button >
</ div > video {
width : 100 % ;
max-width : 320 px ;
border-radius : 8 px ;
background : #0f172a ;
display : block ;
}
.cam-status {
font-size : .8 rem ;
color : #64748b ;
margin : .5 rem 0 ;
}
.btns {
display : flex ;
gap : .5 rem ;
}
.btn {
padding : .4 rem .9 rem ;
border : none ;
border-radius : 6 px ;
cursor : pointer ;
font-size : .8 rem ;
}
.btn-start {
background : #6366f1 ;
color : white ;
}
.btn-stop {
background : white ;
color : #6366f1 ;
border : 1 px solid #6366f1 ;
} setTimeout (() => {
const video = document. getElementById ( "cam-video" );
const status = document. getElementById ( "cam-status" );
const startBtn = document. getElementById ( "cam-start" );
const stopBtn = document. getElementById ( "cam-stop" );
if ( ! video || ! status || ! startBtn || ! stopBtn) return ;
let stream = null ;
startBtn. onclick = async () => {
try {
stream = await navigator.mediaDevices. getUserMedia ({ video: true });
video.srcObject = stream;
status.textContent = "Camera active" ;
} catch (e) {
status.textContent = "Error: " + e.message;
}
};
stopBtn. onclick = () => {
if (stream) {
stream. getTracks (). forEach (( t ) => t. stop ());
video.srcObject = null ;
}
status.textContent = "Camera stopped" ;
};
}, 0 );
const stream = await navigator.mediaDevices. getUserMedia ({ video: true })
videoEl.srcObject = stream
// turn the camera off
stream. getTracks (). forEach ( track => track. stop ())
08. Microphone
AudioContext lets you analyse sound in real time. requestAnimationFrame redraws the meter ~60fps.
Preview index.html style.css script.js
< div class = "mic-status" id = "mic-status" >Microphone not started</ div >
< div class = "mic-meter-wrap" >< div class = "mic-meter" id = "mic-meter" ></ div ></ div >
< div class = "btns" >
< button class = "btn btn-start" id = "mic-start" >Request mic</ button >
< button class = "btn btn-stop" id = "mic-stop" >Stop</ button >
</ div > .mic-meter-wrap {
background : #f1f5f9 ;
border-radius : 99 px ;
height : 1.5 rem ;
margin : .75 rem 0 ;
overflow : hidden ;
}
.mic-meter {
height : 100 % ;
background : #6366f1 ;
border-radius : 99 px ;
width : 0 ;
transition : width .08 s ;
}
.mic-status {
font-size : .8 rem ;
color : #64748b ;
}
.btns {
display : flex ;
gap : .5 rem ;
}
.btn {
padding : .4 rem .9 rem ;
border : none ;
border-radius : 6 px ;
cursor : pointer ;
font-size : .8 rem ;
}
.btn-start {
background : #6366f1 ;
color : white ;
}
.btn-stop {
background : white ;
color : #6366f1 ;
border : 1 px solid #6366f1 ;
} setTimeout (() => {
const meter = document. getElementById ( "mic-meter" );
const status = document. getElementById ( "mic-status" );
const startBtn = document. getElementById ( "mic-start" );
const stopBtn = document. getElementById ( "mic-stop" );
if ( ! meter || ! status || ! startBtn || ! stopBtn) return ;
let audioCtx = null ;
let analyser = null ;
let rafId = null ;
let stream = null ;
startBtn. onclick = async () => {
try {
stream = await navigator.mediaDevices. getUserMedia ({ audio: true });
audioCtx = new AudioContext ();
analyser = audioCtx. createAnalyser ();
audioCtx. createMediaStreamSource (stream). connect (analyser);
const data = new Uint8Array (analyser.frequencyBinCount);
status.textContent = "Listening..." ;
const draw = () => {
analyser. getByteFrequencyData (data);
const avg = data. reduce (( a , b ) => a + b, 0 ) / data. length ;
meter.style.width = Math. min ( 100 , (avg / 128 ) * 100 ) + "%" ;
rafId = requestAnimationFrame (draw);
};
draw ();
} catch (e) {
status.textContent = "Error: " + e.message;
}
};
stopBtn. onclick = () => {
cancelAnimationFrame (rafId);
if (stream) stream. getTracks (). forEach (( t ) => t. stop ());
if (audioCtx) audioCtx. close ();
meter.style.width = "0" ;
status.textContent = "Stopped" ;
};
}, 0 );
const stream = await navigator.mediaDevices. getUserMedia ({ audio: true })
const ctx = new AudioContext ()
const source = ctx. createMediaStreamSource (stream)
const analyser = ctx. createAnalyser ()
source. connect (analyser)
const data = new Uint8Array (analyser.frequencyBinCount)
function draw () {
analyser. getByteFrequencyData (data)
const avg = data. reduce (( a , b ) => a + b, 0 ) / data. length
meter.style.width = (avg / 255 * 100 ) + "%"
requestAnimationFrame (draw)
}
draw ()
09. Touch
Works on phones, tablets, and drawing tablets. e.touches is a list — one entry per active finger.
Preview index.html style.css script.js
< div class = "touch-area" id = "touch-area" >
< div class = "touch-dot" id = "touch-dot" ></ div >
</ div >
< div class = "out" id = "touch-out" >Touch the area above</ div >
< p class = "hint" >Works on mobile/tablet. On desktop, mouse events fire instead.</ p > .touch-area {
width : 100 % ;
height : 160 px ;
background : #f1f5f9 ;
border-radius : 12 px ;
position : relative ;
touch-action : none ;
overflow : hidden ;
border : 2 px dashed #e2e8f0 ;
}
.touch-dot {
position : absolute ;
width : 40 px ;
height : 40 px ;
border-radius : 50 % ;
background : #6366f1 ;
opacity : 0 ;
transform : translate ( -50 % , -50 % );
pointer-events : none ;
transition : opacity .15 s ;
}
.out {
margin-top : .5 rem ;
font-family : monospace ;
font-size : .8 rem ;
color : #64748b ;
}
.hint {
font-size : .75 rem ;
color : #94a3b8 ;
margin-top : .25 rem ;
} setTimeout (() => {
const area = document. getElementById ( "touch-area" );
const dot = document. getElementById ( "touch-dot" );
const out = document. getElementById ( "touch-out" );
if ( ! area || ! dot || ! out) return ;
const showDot = ( x , y ) => {
const rect = area. getBoundingClientRect ();
dot.style.left = x - rect.left + "px" ;
dot.style.top = y - rect.top + "px" ;
dot.style.opacity = "0.8" ;
};
area. addEventListener ( "touchstart" , ( e ) => {
e. preventDefault ();
const t = e.touches[ 0 ];
showDot (t.clientX, t.clientY);
out.textContent = `touches: ${ e . touches . length } | x: ${ Math . round ( t . clientX ) }, y: ${ Math . round ( t . clientY ) }` ;
}, { passive: false });
area. addEventListener ( "touchmove" , ( e ) => {
e. preventDefault ();
const t = e.touches[ 0 ];
showDot (t.clientX, t.clientY);
out.textContent = `moving - touches: ${ e . touches . length } | x: ${ Math . round ( t . clientX ) }, y: ${ Math . round ( t . clientY ) }` ;
}, { passive: false });
area. addEventListener ( "touchend" , () => {
dot.style.opacity = "0" ;
out.textContent = "touchend fired" ;
});
area. addEventListener ( "mousemove" , ( e ) => {
showDot (e.clientX, e.clientY);
out.textContent = `mousemove (desktop) - x: ${ e . offsetX }, y: ${ e . offsetY }` ;
});
area. addEventListener ( "mouseleave" , () => {
dot.style.opacity = "0" ;
});
}, 0 );
area. addEventListener ( "touchstart" , ( e ) => {
console. log (e.touches. length ) // number of fingers
})
area. addEventListener ( "touchmove" , ( e ) => {
e. preventDefault ()
const t = e.touches[ 0 ]
console. log (t.clientX, t.clientY)
}, { passive: false })
area. addEventListener ( "touchend" , () => {
// all fingers lifted
})
10. Clipboard
Copy text with navigator.clipboard.writeText(). Listen for paste to read what the user pastes. Requires HTTPS or localhost.
Preview index.html style.css script.js
< div class = "cb-demo" >
< div class = "section" >
< div class = "sec-label" >navigator.clipboard.writeText()</ div >
< div class = "row" >
< span class = "code-chip" id = "cb-text" >hsl(247, 70%, 55%)</ span >
< button class = "btn" id = "cb-copy" >Copy</ button >
</ div >
< div class = "out" id = "cb-copy-out" >-</ div >
</ div >
< div class = "section" >
< div class = "sec-label" >paste event - paste into the field below</ div >
< input class = "paste-target" id = "cb-paste-target" placeholder = "Paste here (Cmd/Ctrl+V)..." />
< div class = "out" id = "cb-paste-out" >-</ div >
</ div >
</ div > .cb-demo {
display : flex ;
flex-direction : column ;
gap : .75 rem ;
}
.section {
background : #f8fafc ;
border-radius : 8 px ;
padding : .6 rem .9 rem ;
}
.sec-label {
font-size : .75 rem ;
font-family : monospace ;
font-weight : 600 ;
color : #64748b ;
margin-bottom : .4 rem ;
}
.row {
display : flex ;
gap : .5 rem ;
align-items : center ;
}
.code-chip {
font-family : monospace ;
font-size : .875 rem ;
background : #e0e7ff ;
padding : .3 rem .6 rem ;
border-radius : 4 px ;
color : #3730a3 ;
}
.btn {
padding : .35 rem .75 rem ;
background : #6366f1 ;
color : white ;
border : none ;
border-radius : 6 px ;
cursor : pointer ;
font-size : .8 rem ;
}
.out {
font-family : monospace ;
font-size : .8 rem ;
margin-top : .4 rem ;
color : #64748b ;
}
.paste-target {
padding : .4 rem .75 rem ;
border : 1 px solid #e2e8f0 ;
border-radius : 6 px ;
font-family : monospace ;
font-size : .8 rem ;
width : 100 % ;
} setTimeout (() => {
const copyOut = document. getElementById ( "cb-copy-out" );
const pasteOut = document. getElementById ( "cb-paste-out" );
const text = document. getElementById ( "cb-text" );
const copyBtn = document. getElementById ( "cb-copy" );
const pasteTarget = document. getElementById ( "cb-paste-target" );
if ( ! copyOut || ! pasteOut || ! text || ! copyBtn || ! pasteTarget) return ;
const fallbackCopy = ( value ) => {
const ta = document. createElement ( "textarea" );
ta.value = value;
ta. setAttribute ( "readonly" , "" );
ta.style.position = "fixed" ;
ta.style.opacity = "0" ;
ta.style.pointerEvents = "none" ;
document.body. appendChild (ta);
ta. select ();
const ok = document. execCommand ( "copy" );
document.body. removeChild (ta);
return ok;
};
copyBtn. onclick = async () => {
const value = text.textContent;
try {
if ( ! navigator.clipboard?.writeText) {
const ok = fallbackCopy (value);
copyOut.textContent = ok
? "Copied with fallback (execCommand)."
: "Copy failed - use Cmd/Ctrl+C manually." ;
return ;
}
await navigator.clipboard. writeText (value);
copyOut.textContent = "Copied! Now paste it somewhere." ;
} catch (e) {
const blocked = / permissions policy | NotAllowedError | blocked / i . test ( String (e?.message || e));
if (blocked) {
const ok = fallbackCopy (value);
copyOut.textContent = ok
? "Clipboard API blocked, copied with fallback (execCommand)."
: "Clipboard blocked by policy. Use Cmd/Ctrl+C manually." ;
return ;
}
copyOut.textContent = "Error: " + e.message;
}
};
pasteTarget. addEventListener ( "paste" , ( e ) => {
const pasted = e.clipboardData. getData ( "text/plain" );
pasteOut.textContent = `pasted: "${ pasted }"` ;
});
}, 0 );
await navigator.clipboard. writeText ( "Hello!" )
document. addEventListener ( "paste" , ( e ) => {
const text = e.clipboardData. getData ( "text/plain" )
console. log (text)
})