Build a Virtual Keyboard using HTML CSS & JavaScript
In this article, we will build a virtual keyboard using HTML, CSS, and JavaScript. A virtual keyboard is a user interface component that allows users to input text by clicking on the keys displayed on the screen. Our keyboard will support standard alphanumeric characters, special characters, and a Caps Lock feature.
Preview of final output: Let us have a look at how the final output will look like.
Prerequisites:
Approach:
- Create an HTML file (index.html) to serve as the main structure for the virtual keyboard.
- Include a <textarea> element where the typed text will appear.
- Add a <div> element to represent the virtual keyboard. We’ll generate the keyboard keys dynamically using JavaScript.
- Create a style.css file to style the virtual keyboard and its keys.
- Define styles for the keyboard layout, keys, background colors, fonts, and any other visual elements.
- Customize the appearance to match your design preferences.
- Create a script.js file to handle the functionality of the virtual keyboard.
- Dynamically generate the keyboard keys using JavaScript and insert them into the <div> element with the id “keyboard.”
- Add event listeners to the keys to capture click events.
- Handle key clicks and update the text in the <textarea> accordingly.
- Implement the Caps Lock feature to toggle between uppercase and lowercase characters.
Example: Below is the implementation of project
Javascript
// script.js const Keyboard = { elements: { main: null , keysContainer: null , keys: [], capsKey: null , }, properties: { value: "" , capsLock: false , keyboardInputs: null , keyLayout: [ "1" , "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9" , "0" , "backspace" , "q" , "w" , "e" , "r" , "t" , "y" , "u" , "i" , "o" , "p" , "caps" , "a" , "s" , "d" , "f" , "g" , "h" , "j" , "k" , "l" , "enter" , "done" , "z" , "x" , "c" , "v" , "b" , "n" , "m" , "," , "." , "?" , "space" , ], }, init() { // create and setup main element this .elements.main = document.createElement( "div" ); this .elements.main.classList .add( "keyboard" , "keyboard--hidden" ); document.body .appendChild( this .elements.main); // create and setup child container component this .elements.keysContainer = document.createElement( "div" ); this .elements.keysContainer .classList.add( "keyboard__keys" ); this .elements.main .appendChild( this .elements.keysContainer); // create and setup key elements this .elements.keysContainer .appendChild( this ._createKeys()); this .elements.keys = this .elements.keysContainer .querySelectorAll( ".keyboard__key" ); // open keyboard for elements with .use-keyboard-input this .properties.keyboardInputs = document.querySelectorAll( ".use-keyboard-input" ); this .properties .keyboardInputs .forEach((element) => { element.addEventListener( "focus" , () => { this .open(element.value, (currentValue) => { element.value = currentValue; }); }); }); }, _createIconHTML(icon_name) { return `<span class= "material-icons" >${icon_name}</span>`; }, _createKeyBtn(iconName, class1, onclick, class2) { this .keyElement = document.createElement( "button" ); // add common attributes and classes this .keyElement .setAttribute( "type" , "button" ); this .keyElement .classList.add( "keyboard__key" ); // add specific listeners and classes this .keyElement .classList.add(class1, class2); this .keyElement.innerHTML = this ._createIconHTML(iconName); this .keyElement .addEventListener( "click" , onclick); }, _createKeys() { const fragment = document.createDocumentFragment(); this .properties.keyLayout.forEach((key) => { const insertLineBreak = [ "backspace" , "p" , "enter" , "?" ].indexOf(key) !== -1; switch (key) { case "backspace" : this ._createKeyBtn( "backspace" , "keyboard__key--wide" , () => { this .properties.value = this .properties.value.slice(0, -1); this ._updateValueInTarget(); }); break ; case "caps" : this ._createKeyBtn( "keyboard_capslock" , "keyboard__key--activatable" , () => { this .elements.capsKey .classList .toggle( "keyboard__key--active" ); this ._toggleCapsLock(); }, "keyboard__key--wide" ); this .elements.capsKey = this .keyElement; break ; case "enter" : this ._createKeyBtn( "keyboard_return" , "keyboard__key--wide" , () => { this .properties.value += "\n" ; this ._updateValueInTarget(); }); break ; case "space" : this ._createKeyBtn( "space_bar" , "keyboard__key--extra--wide" , () => { this .properties.value += " " ; this ._updateValueInTarget(); }); break ; case "done" : this ._createKeyBtn( "check_circle" , "keyboard__key--dark" , () => { this .close(); this ._updateValueInTarget(); }, "keyboard__key--wide" ); break ; default : this ._createKeyBtn(); this .keyElement.textContent = key.toLowerCase(); this .keyElement .addEventListener( "click" , () => { this .properties.value += this .properties.capsLock ? key.toUpperCase() : key.toLowerCase(); this ._updateValueInTarget(); }); break ; } fragment.appendChild( this .keyElement); if (insertLineBreak) { fragment .appendChild(document.createElement( "br" )); } }); return fragment; }, _updateValueInTarget() { this .properties .keyboardInputs .forEach((keyboard) => { keyboard.value = this .properties.value; }); }, _toggleCapsLock() { this .properties.capsLock = ! this .properties.capsLock; for (let key of this .elements.keys) { if (key.childElementCount === 0) { key.textContent = this .properties.capsLock ? key.textContent.toUpperCase() : key.textContent.toLowerCase(); } } }, open(initialValue, oninput) { this .properties.value = initialValue || "" ; this .elements.main .classList .remove( "keyboard--hidden" ); }, close() { this .properties.value = this .properties.value; this .elements.main .classList.add( "keyboard--hidden" ); }, }; window.addEventListener( "DOMContentLoaded" , function () { Keyboard.init(); }); |
HTML
<!-- index.html --> <!DOCTYPE html> < html lang = "en" > < head > < meta name = "viewport" content=" width = device -width, initial-scale = 1 .0" /> < link rel = "stylesheet" href = "style.css" /> < link href = "https://fonts.googleapis.com/icon?family=Material+Icons" rel = "stylesheet" /> < title > Virtual Keyboard </ title > </ head > < body > < textarea name = "anytext" class = "text-area use-keyboard-input" placeholder = "Click me!" > </ textarea > < script src = "script.js" ></ script > </ body > </ html > |
CSS
/* style.css */ .text-area { padding : 5px ; width : calc( 100 vw - 30px ); height : calc( 100 vh - 295px ); } .keyboard { position : fixed ; left : 0 ; bottom : 0 ; width : 100% ; padding : 5px 0 ; background : #004134 ; box-shadow: 0 0 50px rgba( 0 , 0 , 0 , 0.5 ); transition: bottom 0.4 s; } .keyboard-- hidden { bottom : -100% ; } .keyboard__keys { text-align : center ; } .keyboard__key { height : 45px ; width : 6% ; max-width : 90px ; margin : 3px ; border-radius: 4px ; border : none ; background : rgba( 255 , 255 , 255 , 0.2 ); color : white ; font-size : 1.05 rem; outline : none ; cursor : pointer ; display : inline-flex; justify- content : center ; align-items: center ; vertical-align : top ; padding : 0 ; -webkit-tap-highlight- color : transparent ; position : relative ; } .keyboard__key:active { background : rgba( 255 , 255 , 255 , 0.12 ); } .keyboard__key--wide { width : 12% ; } .keyboard__key--extra--wide { width : 36% ; max-width : 500px ; } .keyboard__key--activatable::after { content : "" ; position : absolute ; top : 10px ; right : 10px ; width : 8px ; height : 8px ; background : rgba( 0 , 0 , 0 , 0.4 ); border-radius: 50% ; } .keyboard__key--active::after { background : #08ff00 ; } .keyboard__key--dark { background : rgba( 0 , 0 , 0 , 0.25 ); } |
Output:
Contact Us