├── .gitignore ├── LICENSE-MIT ├── demo.html ├── jquery.onScreenKeyboard.js ├── jquery.onScreenKeyboard.min.js ├── onScreenKeyboard.css ├── package.json └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | jquery.ui.draggable.js 2 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Chris Cook 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | On Screen Keyboard demo 8 | 9 | 33 | 34 | 35 | 36 | 37 |
38 | 39 | 40 | 41 | 42 |
43 | 44 | 45 | 46 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /jquery.onScreenKeyboard.js: -------------------------------------------------------------------------------- 1 | /** 2 | * On-Screen Keyboard jQuery Plugin 3 | * 4 | * Provides users with a fluid-width on-screen keyboard. 5 | * 6 | * @author Chris Cook 7 | * @license MIT 8 | * @version 1.0.2 9 | */ 10 | 11 | (function ($) { 12 | 13 | 'use strict'; 14 | 15 | $.fn.onScreenKeyboard = function (options) { 16 | 17 | var settings = $.extend({ 18 | draggable: false, 19 | rewireReturn: false, 20 | rewireTab: false, 21 | topPosition: '20%', 22 | leftPosition: '30%' 23 | }, options); 24 | var $keyboardTriggers = this; 25 | var $input = $(); 26 | var $keyboard = renderKeyboard('osk-container'); 27 | var $keys = $keyboard.children('li'); 28 | var $letterKeys = $keyboard.children('li.osk-letter'); 29 | var $symbolKeys = $keyboard.children('li.osk-symbol'); 30 | var $numberKeys = $keyboard.children('li.osk-number'); 31 | var $returnKey = $keyboard.children('li.osk-return'); 32 | var $tabKey = $keyboard.children('li.osk-tab'); 33 | var shift = false; 34 | var capslock = false; 35 | var inputOptions = []; 36 | var browserInPercent = $tabKey.css('marginRight').indexOf('%') > -1; 37 | 38 | /** 39 | * Focuses and customises the keyboard for the current input object. 40 | * 41 | * @param {jQueryObject} The input object to focus on. 42 | */ 43 | function activateInput($input) { 44 | var inputOptionsString = $input.attr('data-osk-options'); 45 | $keys.removeClass('osk-disabled'); 46 | $keyboardTriggers.removeClass('osk-focused'); 47 | if (inputOptionsString !== undefined) { 48 | inputOptions = inputOptionsString.split(' '); 49 | if ($.inArray('disableSymbols', inputOptions) > -1) { 50 | $symbolKeys.addClass('osk-disabled'); 51 | } 52 | if ($.inArray('disableTab', inputOptions) > -1) { 53 | $tabKey.addClass('osk-disabled'); 54 | } 55 | if ($.inArray('disableReturn', inputOptions) > -1) { 56 | $returnKey.addClass('osk-disabled'); 57 | } 58 | 59 | } 60 | $input.addClass('osk-focused').focus(); 61 | } 62 | 63 | /** 64 | * Fixes the width of the keyboard in browsers which round down part-pixel 65 | * values (all except Firefox). Most browsers which do this return CSS 66 | * margins in pixels rather than percent, so this is used to determine 67 | * whether or not to use this function. Opera does not however, so for now 68 | * this function does not work in that browser. 69 | */ 70 | function fixWidths() { 71 | var $key = $(), 72 | keyboardWidth = $keyboard.width(), 73 | totalKeysWidth = 0, 74 | difference; 75 | if (browserInPercent) { 76 | $keys.each(function () { 77 | $key = $(this); 78 | if (!$key.hasClass('osk-dragger') && !$key.hasClass('osk-space')) { 79 | totalKeysWidth += $key.width() + Math.floor((parseFloat($key.css('marginRight')) / 100) * keyboardWidth); 80 | if ($key.hasClass('osk-last-item')) { 81 | difference = keyboardWidth - totalKeysWidth; 82 | if (difference > 0) { 83 | $key.width($key.width() + difference); 84 | } 85 | difference = 0; 86 | totalKeysWidth = 0; 87 | } 88 | } 89 | }); 90 | } 91 | } 92 | 93 | if (settings.draggable && jQuery.ui) { 94 | $keyboard.children('li.osk-dragger').show(); 95 | $keyboard.css('paddingTop', '0').draggable({ 96 | containment : 'document', 97 | handle : 'li.osk-dragger' 98 | }); 99 | } 100 | 101 | if (settings.rewireReturn) { 102 | $returnKey.html(settings.rewireReturn); 103 | } 104 | 105 | $keyboard.css('top', settings.topPosition).css('left', settings.leftPosition); 106 | 107 | fixWidths(); 108 | 109 | $keyboard.hide().css('visibility', 'visible'); 110 | 111 | $(window).resize(function () { 112 | fixWidths(); 113 | }); 114 | 115 | $keyboardTriggers.click(function () { 116 | $input = $(this); 117 | activateInput($input); 118 | $keyboard.fadeIn('fast'); 119 | }); 120 | 121 | $keyboard.on('click', 'li', function () { 122 | var $key = $(this), 123 | character = $key.html(), 124 | inputValue, 125 | indexOfNextInput; 126 | 127 | // Disabled keys/dragger 128 | if ($key.hasClass('osk-dragger') || $key.hasClass('osk-disabled')) { 129 | $input.focus(); 130 | return false; 131 | } 132 | 133 | // 'Hide Keyboard' key 134 | if ($key.hasClass('osk-hide')) { 135 | $keyboard.fadeOut('fast'); 136 | $input.blur(); 137 | $keyboardTriggers.removeClass('osk-focused'); 138 | return false; 139 | } 140 | 141 | // 'Shift' key 142 | if ($key.hasClass('osk-shift')) { 143 | $letterKeys.toggleClass('osk-uppercase'); 144 | $.merge($symbolKeys.children('span'), $numberKeys.children('span')).toggle(); 145 | if ($symbolKeys.hasClass('osk-disabled')) { 146 | $numberKeys.toggleClass('osk-disabled'); 147 | } 148 | shift = !shift; 149 | capslock = false; 150 | return false; 151 | } 152 | 153 | // 'Caps Lock' key 154 | if ($key.hasClass('osk-capslock')) { 155 | $letterKeys.toggleClass('osk-uppercase'); 156 | capslock = true; 157 | return false; 158 | } 159 | 160 | // 'Backspace' key 161 | if ($key.hasClass('osk-backspace')) { 162 | inputValue = $input.val(); 163 | $input.val(inputValue.substr(0, inputValue.length - 1)); 164 | $input.trigger('keyup'); 165 | return false; 166 | } 167 | 168 | // Symbol/number keys 169 | if ($key.hasClass('osk-symbol') || $key.hasClass('osk-number')) { 170 | character = $('span:visible', $key).html(); 171 | } 172 | 173 | // Spacebar 174 | if ($key.hasClass('osk-space')) { 175 | character = ' '; 176 | } 177 | 178 | // 'Tab' key - either enter an indent (default) or switch to next form element 179 | if ($key.hasClass('osk-tab')) { 180 | if (settings.rewireTab) { 181 | $input.trigger('onchange'); 182 | indexOfNextInput = $keyboardTriggers.index($input) + 1; 183 | if (indexOfNextInput < $keyboardTriggers.length) { 184 | $input = $($keyboardTriggers[indexOfNextInput]); 185 | } else { 186 | $input = $($keyboardTriggers[0]); 187 | } 188 | activateInput($input); 189 | return false; 190 | } else { 191 | character = '\t'; 192 | } 193 | } 194 | 195 | // 'Return' key - either linebreak (default) or submit form 196 | if ($key.hasClass('osk-return')) { 197 | if (settings.rewireReturn) { 198 | $keyboardTriggers.parent('form').submit(); 199 | return false; 200 | } else { 201 | character = '\n'; 202 | } 203 | } 204 | 205 | // Uppercase keys 206 | if ($key.hasClass('osk-uppercase')) { 207 | character = character.toUpperCase(); 208 | } 209 | 210 | // Handler for when shift is enabled 211 | if (shift) { 212 | $.merge($symbolKeys.children('span'), $numberKeys.children('span')).toggle(); 213 | if (!capslock) { 214 | $letterKeys.toggleClass('osk-uppercase'); 215 | } 216 | if (settings.disableSymbols) { 217 | $numberKeys.toggleClass('osk-disabled'); 218 | } 219 | shift = false; 220 | } 221 | 222 | $input.focus().val($input.val() + character); 223 | $input.trigger('keyup'); 224 | }); 225 | 226 | return this; 227 | 228 | }; 229 | 230 | /** 231 | * Renders the keyboard. 232 | * 233 | * @param {String} id of the keyboard 234 | * @return {jQuery} the keyboard jQuery instance 235 | */ 236 | function renderKeyboard(keyboardId) { 237 | var $keyboard = $('#' + keyboardId); 238 | 239 | if ($keyboard.length) { 240 | return $keyboard; 241 | } 242 | 243 | $keyboard = $( 244 | '' 363 | ); 364 | 365 | $('body').append($keyboard); 366 | 367 | return $keyboard; 368 | } 369 | 370 | })(jQuery); 371 | -------------------------------------------------------------------------------- /jquery.onScreenKeyboard.min.js: -------------------------------------------------------------------------------- 1 | !function(s){"use strict";function l(l){var a=s("#"+l);return a.length?a:(a=s(''),s("body").append(a),a)}s.fn.onScreenKeyboard=function(a){function o(l){var a=l.attr("data-osk-options");k.removeClass("osk-disabled"),n.removeClass("osk-focused"),void 0!==a&&(m=a.split(" "),s.inArray("disableSymbols",m)>-1&&p.addClass("osk-disabled"),s.inArray("disableTab",m)>-1&&u.addClass("osk-disabled"),s.inArray("disableReturn",m)>-1&&d.addClass("osk-disabled")),l.addClass("osk-focused").focus()}function i(){var l,a=s(),o=c.width(),i=0;g&&k.each(function(){a=s(this),a.hasClass("osk-dragger")||a.hasClass("osk-space")||(i+=a.width()+Math.floor(parseFloat(a.css("marginRight"))/100*o),a.hasClass("osk-last-item")&&(l=o-i,l>0&&a.width(a.width()+l),l=0,i=0))})}var e=s.extend({draggable:!1,rewireReturn:!1,rewireTab:!1,topPosition:"20%",leftPosition:"30%"},a),n=this,t=s(),c=l("osk-container"),k=c.children("li"),r=c.children("li.osk-letter"),p=c.children("li.osk-symbol"),f=c.children("li.osk-number"),d=c.children("li.osk-return"),u=c.children("li.osk-tab"),b=!1,h=!1,m=[],g=u.css("marginRight").indexOf("%")>-1;return e.draggable&&jQuery.ui&&(c.children("li.osk-dragger").show(),c.css("paddingTop","0").draggable({containment:"document",handle:"li.osk-dragger"})),e.rewireReturn&&d.html(e.rewireReturn),c.css("top",e.topPosition).css("left",e.leftPosition),i(),c.hide().css("visibility","visible"),s(window).resize(function(){i()}),n.click(function(){t=s(this),o(t),c.fadeIn("fast")}),c.on("click","li",function(){var l,a,i=s(this),k=i.html();if(i.hasClass("osk-dragger")||i.hasClass("osk-disabled"))return t.focus(),!1;if(i.hasClass("osk-hide"))return c.fadeOut("fast"),t.blur(),n.removeClass("osk-focused"),!1;if(i.hasClass("osk-shift"))return r.toggleClass("osk-uppercase"),s.merge(p.children("span"),f.children("span")).toggle(),p.hasClass("osk-disabled")&&f.toggleClass("osk-disabled"),b=!b,h=!1,!1;if(i.hasClass("osk-capslock"))return r.toggleClass("osk-uppercase"),h=!0,!1;if(i.hasClass("osk-backspace"))return l=t.val(),t.val(l.substr(0,l.length-1)),t.trigger("keyup"),!1;if((i.hasClass("osk-symbol")||i.hasClass("osk-number"))&&(k=s("span:visible",i).html()),i.hasClass("osk-space")&&(k=" "),i.hasClass("osk-tab")){if(e.rewireTab)return t.trigger("onchange"),a=n.index(t)+1,t=s(a li { 13 | float: left; 14 | width: 5.81395349%; 15 | height: 3em; 16 | margin: 0 0.726744186% 5px 0; 17 | line-height: 3em; 18 | text-align: center; 19 | list-style-type: none; 20 | cursor: pointer; 21 | -webkit-user-select: none; 22 | -khtml-user-select: none; 23 | -moz-user-select: -moz-none; 24 | -ms-user-select: none; 25 | user-select: none; 26 | } 27 | 28 | #osk-container > .osk-capslock, 29 | #osk-container > .osk-tab, 30 | #osk-container > .osk-shift { 31 | clear: left; 32 | } 33 | 34 | #osk-container > .osk-dragger { 35 | display: none; 36 | width: 100%; 37 | cursor: move; 38 | } 39 | 40 | #osk-container > .osk-dragger:hover { 41 | position: static; 42 | } 43 | 44 | #osk-container > .osk-tab, 45 | #osk-container > .osk-backspace { 46 | width: 14.9709302%; 47 | } 48 | 49 | #osk-container > .osk-capslock { 50 | width: 13.6337209%; 51 | } 52 | 53 | #osk-container > .osk-return { 54 | width: 13.6918605%; 55 | } 56 | 57 | #osk-container > .osk-shift { 58 | width: 16.2381395%; 59 | } 60 | 61 | #osk-container > .osk-hide { 62 | width: 17.5581396%; 63 | } 64 | 65 | #osk-container > .osk-space { 66 | clear: left; 67 | width: 100%; 68 | margin: 0; 69 | } 70 | 71 | #osk-container > .osk-last-item { 72 | margin-right: 0 !important; 73 | } 74 | 75 | #osk-container .osk-on { 76 | display: none; 77 | } 78 | 79 | #osk-container > .osk-uppercase { 80 | text-transform: uppercase; 81 | } 82 | 83 | #osk-container > .osk-disabled { 84 | cursor: default; 85 | } 86 | 87 | #osk-container > .osk-disabled:hover { 88 | position: static; 89 | } 90 | 91 | #osk-container > li:hover { 92 | position: relative; 93 | top: 1px; 94 | } 95 | 96 | /*-----[Customisable styles]--------------------------------------------------*/ 97 | 98 | #osk-container { 99 | width: 47%; /* Account for 2% padding and a vertical scroll bar */ 100 | min-width: 500px; 101 | max-width: 1200px; 102 | background: #EEE; 103 | border-radius: 5%; 104 | box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); 105 | } 106 | 107 | #osk-container > li { 108 | background: #DDD; 109 | border-radius: 10%; 110 | -moz-transition: 0.5s; 111 | } 112 | 113 | #osk-container > .osk-dragger { 114 | background: transparent; 115 | color: #AAA; 116 | } 117 | 118 | #osk-container > .osk-disabled { 119 | background: #E5E5E5; 120 | color: #CCC; 121 | } 122 | 123 | .osk-focused { 124 | background: #FAFAFA !important; 125 | } 126 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "onScreenKeyboard", 3 | "version": "1.0.2", 4 | "title": "On-Screen Keyboard jQuery plug-in", 5 | "description": "Provides users with a fluid-width on-screen keyboard.", 6 | "homepage": "https://github.com/chriscook/on-screen-keyboard", 7 | "author": { 8 | "name": "Chris Cook", 9 | "email": "chris@chris-cook.co.uk", 10 | "url": "http://chris-cook.co.uk" 11 | }, 12 | "license": "MIT", 13 | "keywords": [ 14 | "ecosystem:jquery", 15 | "jquery-plugin", 16 | "keyboard", 17 | "security", 18 | "accessibility", 19 | "touchscreen", 20 | "touch" 21 | ], 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/chriscook/on-screen-keyboard.git" 25 | }, 26 | "bugs": { 27 | "email": "chris@chris-cook.co.uk", 28 | "url": "https://github.com/chriscook/on-screen-keyboard/issues" 29 | }, 30 | "dependencies": { 31 | "jquery": ">=1.5" 32 | }, 33 | "optionalDependencies": { 34 | "jquery-ui": ">=1.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # On Screen Keyboard 2 | 3 | ## Version 1.0.2 4 | 5 | ### Introduction 6 | 7 | __On-Screen Keyboard__ is a _jQuery_ plug-in which allows a fluid-width keyboard to be launched when the user clicks on an element. 8 | 9 | ### Demo 10 | 11 | A demo is available [here](http://chriscook.github.com/on-screen-keyboard/). 12 | 13 | ### How to use it 14 | 15 | 1. Add `jquery.onScreenKeyboard.js` and `onScreenKeyboard.css` to your project, along with _jQuery_. Optionally also add _jQuery UI_ with the _Draggables_ widget if you would like your users to be able to move the keyboard around the screen. 16 | 2. Create your form or input elements. 17 | 3. Add the following JavaScript to your page, to be executed on load: 18 | 19 | ```javascript 20 | $('.osk-trigger').onScreenKeyboard(); 21 | ``` 22 | 23 | ...where `.osk-trigger` is a selector for the input elements you would like to trigger the keyboard. 24 | 25 | A demo is available in demo.html. 26 | 27 | ### Additional settings 28 | 29 | Additional settings can be used to customise the keyboard, and should be added as a parameter within curly braces: 30 | 31 | + `draggable`: __Requres jQuery UI with Draggables__ Whether or not the keyboard is movable (default `false`; must be boolean). 32 | + `rewireReturn`: Make the return key submit the form instead of inserting a linebreak (default `false`; must be either boolean false or a string to replace `'return'` with on the key). 33 | + `rewireTab`: Make the tab key cycle through input elements in the order they appear in the DOM instead of inserting a tab (default `false`; must be boolean). 34 | + `topPosition`: The `top` CSS property of the keyboard (default `20%`; must be a string suitable for CSS, i.e. one ending in a measurement unit). 35 | + `leftPosition`: The `left` CSS property of the keyboard (default `30%`; must be a string suitable for CSS, i.e. one ending in a measurement unit). 36 | 37 | An example of these in practice: 38 | 39 | ```javascript 40 | $('.osk-trigger').onScreenKeyboard({ 41 | 'draggable': true, 42 | 'rewireReturn': 'search', 43 | 'rewireTab': true 44 | }); 45 | ``` 46 | 47 | In addition to these universal settings, you can change the keyboard on an input-by-input basis using the following parameters added to your input elements under the attribute `data-osk-options`, separated by spaces: 48 | 49 | + `disableSymbols` allows you to disable the symbol keys. 50 | + `disableTab` allows you to disable the tab key. 51 | + `disableReturn` allows you to disable the return key. 52 | 53 | An example of these in practice: 54 | 55 | ```html 56 | 57 | ``` 58 | 59 | `jquery.onscreenkeyboard.js` contains the HTML for the keyboard at the bottom. As long as class names remain the same, this can be changed however much you like. The keyboard can only be written manually (e.g. custom non-us keyboard layouts) - the plugin is able to pick it up. Keep in mind that the character entered into the input box is taken directly from the contents of the `li` element for the pressed key (with exceptions for special keys such as return and tab, when not overridden, and backspace). 60 | 61 | `onscreenkeyboard.css` can also be edited to customise the keyboard's design. The first section, "Keyboard Structure" should be mostly left alone. The second section contains definitions for colour and keyboard position. 62 | 63 | ### Compatibility 64 | 65 | + Internet Explorer 7+ 66 | + Firefox 3+ 67 | + Chrome 68 | + Opera (see note below) 69 | + Safari 70 | 71 | ### Issues 72 | 73 | + In Opera, the widths of keys on the upper three rows will not match that of the space bar. This is due to the way Opera deals with sub-pixel values. 74 | 75 | ### Author and Acknowledgements 76 | 77 | + Written by [Chris Cook](http://chris-cook.co.uk) 78 | + Based upon [this tutorial from nettuts+](http://net.tutsplus.com/tutorials/javascript-ajax/creating-a-keyboard-with-css-and-jquery/) 79 | --------------------------------------------------------------------------------