Skip to content

New UI widgets: textpopup and hebrewKeyboard

Last modified 2011-10-28; added box option

The Hebrew pop-up keyboard on the YI site search box was always hard-coded and kind of obtrusive, so I wanted to make a jQuery plugin to add a keyboard to any input type="text" or textarea. To make it more flexible, I factored it into a general-purpose popup widget and the keyboard itself.

Download the code.


The $.ui.textpopup widget is meant to be a base class for real popup widgets. It requires jQuery UI 1.7 and my subclassing code. The subclass needs to override the _fill method to fill the box with the desired controls. For example:

<div id="example1">
	Example 1: <input>

$.widget("ui.helloWorld", $.bililite.textpopup, {
	_fill: function(box){
		var self = this;
		$('<input type="button" value="Hello, World" />').appendTo(box).click(function() {
			self.element.val('Hello, World')

$('#example1 input').helloWorld();

textpopup has the following options:

show {Function}
Called to show the popup; this is set to the popup div and the speed is the argument(s) (this is meant to be a function called like $
hide {Function}
Called to show the popup; this is set to the popup div and the speed is the argument(s) (this is meant to be a function called like $.fn.hide)
box {jQuery object|undefined}
Element to contain the popup box. If undefined, a new, absolutely-positioned <div> is created. Set this to create an "inline" popup.
shown {Function}
Callback (this is set to the input element) called when show is completed Also available as the custom event textpopupshown
hidden {Function}
Callback (this is set to the input element) called when hide is completed Also available as the custom event textpopuphidden
created {Function}
Callback (this is set to the input element) called when the popup is created. Signature is Function(Event, box {jQuery object}) Also available as the custom event textpopupcreated
speed {String|Number|Array}
Parameter to pass to show/hide above. If it is an array, it is passed with apply so you can pass multiple arguments. For instance, to use jQuery UI effects, use speed: ['slide', {direction: 'right'}, 'slow']
hideOnOutsideClick {Boolean}
true to hide the popup if the mouse is clicked outside the popup or the target element
position {'tl'|'lt|'bl'|'lb'|'tr'|'rt'|'br'|'rb'|Object}
String that indicates where to position the popup relative to the textbox. 'tl' means on top of the input box, aligned to the left edge; 'lt' means on the left of the input box aligned to the top edge; similarly 'b' for bottom and 'r' for right. If an object is passed, it will be passed unchanged to position, except that if the of option is not specified, it will be set to this.element
trigger {'self'|null|String|jQuery|DOM element}
Element(s) to use as the trigger to show the popup. uses the code if (trigger == 'self') trigger = self.element; if (trigger) $(trigger).click(function(){})
class {String}
class to be added to the popup box

// Default values
$.ui.textpopup.prototype.options = {
	show: $,
	hide: $.fn.hide,
	speed: 'slow',
	hideOnOutsideClick: true,
	position: 'tl',
	trigger: 'self',
	'class': 'ui-textpopup-box'

Note that the event names prepend the actual name of the widget, so if you subclass textpopup, use the new name. Thus $.ui.textpopup.subclass('foo'); $(selector).foo().bind('fooshown', function(){});.

Examples of position:

<div id="example2">

$('#example2 input').each(function(){
  $(this).css({height: '50px'}).helloWorld({position: $(this).prev().text()});

$('#example2 div, #example2 span').css({margin:'2px'});

<div id="example3">
<div><strong>Slide Right</strong><input/></div>

$('#example3 input').each(function(){
  $(this).css({height: '50px'}).helloWorld({
    speed: ['slide', {direction: 'right'}, 'slow'],
    position: {my: 'bottom', at: 'top'}

$('#example3 div, #example3 span').css({margin:'2px'});

Callable functions include (obviously, use the name of the subclass you created):

$(...).textpopup('show') and $(...).helloWorld('hide')
show and hide the popup. These return the jQuery object.
returns the popup div as a single-element jQuery object
repositions the popup. Use this if the size of the popup changes. Returns the jQuery object.

Example of using callable functions and the trigger option:

<div id="example4">
	<label>Example 4 hidden:</label><br/><input id="input4">

$('<span> Hide </span>').css({outline: 'black dotted thin'}).insertAfter('#input4').click (function(){
var triggerElement = $('<span> Show </span>').css({outline: 'black dotted thin'}).insertAfter('#input4');
  hideOnOutsideClick: false,
  position: {my: 'right', at: 'left'},
  speed: ['fold', {}, 1000],
  trigger: triggerElement,
  shown: function() {$('#example4 label').text('Example 4 visible:')},
  hidden: function() {$('#example4 label').text('Example 4 hidden:')}
$('#input4').helloWorld('box').css('border', '2px outset #eee');


The $.bililite.hebrewKeyboard is just a subclass of $.bililite.textpopup that uses AJAX to load a Hebrew keyboard into the popup div and $.fn.sendkeys to insert the Hebrew characters.

Inspired by Ilya Lebedev's VirtualKeyboard, the plugin also catches keystrokes to allow typing directly in Hebrew, using the CapsLock key as a toggle between English and Hebrew keyboards.

<div id="example5">
	Example 5 (Hebrew): <input id="input5">


It works with contenteditable="true" elements as well:

<div id="example6" contenteditable="true" >
	Example 6 (Hebrew): Edit me!


Download the html for the keyboard.

The keyboard uses CSS sprites and a single image:

hebrew keyboard

to simulate the keypresses. IE (of course) can't handle a:hover styling unless there's a real link, so each key has a pretend href="javascript:insertLetter", but really uses $('a').click(function(){sendkeys...return false;})

{ 7 } Comments

  1. Fatal error: Uncaught Error: Call to undefined function ereg() in /home/public/blog/wp-content/themes/barthelme/functions.php:178 Stack trace: #0 /home/public/blog/wp-content/themes/barthelme/comments.php(34): barthelme_commenter_link() #1 /home/public/blog/wp-includes/comment-template.php(1469): require('/home/public/bl...') #2 /home/public/blog/wp-content/themes/barthelme/single.php(44): comments_template() #3 /home/public/blog/wp-includes/template-loader.php(74): include('/home/public/bl...') #4 /home/public/blog/wp-blog-header.php(19): require_once('/home/public/bl...') #5 /home/public/blog/index.php(17): require('/home/public/bl...') #6 {main} thrown in /home/public/blog/wp-content/themes/barthelme/functions.php on line 178