Archive for the ‘jQuery’ Category

I've finally decided to join the 21st century and use automated testing on my bililiteRange code; I'm using QUnit, with tests on github. I want to bililiteRange test on all three types of editable elements, <input type=text>, <textarea>, and <div contenteditable>, so I created a convenience function to run each test on each type of element, with

var elements = ['div', 'textarea', 'input'];
function multitest (message, fn){
  elements.forEach( function(name){
    test(message, function(){
      var $el = $('<'+name'>').attr('contenteditable','').appendTo('#qunit-fixture');
      fn($el); // the testing function
    });
  });
}

And the testing code calls multitest rather than test.

I feel so modern; maybe I'll start complaining about waterfalls next.

Playing with the kavanot editor, I wanted some way to encapsulate the process of noting that the text is "dirty"—meaning has been changed but not yet saved, and using a Promise to express the idea that the text is soon-to-be-saved, then saved.

I created a plugin that simply represents a state machine with four states, clean, dirty, pending, and failed. So you can do

var monitor = $.savemonitor();
$('textarea').on('input', function() { monitor.dirty() }); // track when the text area changes
// call this function to save (say, as a click handler for a Save button
function save(){
  var text = $('textarea').val();
  monitor.clean($.post('saveit.php', {data: text})); // jQuery Deferred's like $.post can be cast to Promises
}
// and keep track of the status
monitor.on('clean', function() {console.log('saved')})
monitor.on('failed', function() {console.log('not saved')})

See the code on github.

See a simple demo.

See a demo that uses FontAwesome icons and randomly fails to simulate a network failure.

Continue reading ‘New jQuery plugin, savemonitor’ »

As promised, I have updated the status plugin to use Promises. Unfortunately, that required a small change to the parameters. Since I doubt anyone is using it, that will not likely affect anyone but me. The version is now 1.1.

This plugin is obsolete. See the new status at github.bililite.com/status.

Updated 2015-01-19 to allow $(console).status(). Now at version 1.4.

Updated 2013-12-18 to use Promises. This necessitated removing the result option and adding the returnPromise.

Updated 2014-01-09 to add initialText option and to document the markup used.

For the Kavanot editor I wanted to have a way to display messages (like "Text saved successfully") that would show up on screen then fade out, and a way to enter text (like the title, or editing commands if I ever get that working). I packaged that all together into a plugin could handle both, with an anticipated use like:

<textarea id=editor></textarea>
<div id=notifications></div>

And then do something like $('#notifications').status('Text saved'); and that briefly shows the message in the <div> and then gradually fade out.

And for user input,

$('#notifications').status({
  prompt: 'File Name:',
  run: function (text) { 
    /* do something with text */
    if (successful) return 'success message';
    if (! successful) throw {message: 'failure message'};
  }
});

displays the prompt and an input box then runs the function and displays the result (it automatically catches the thrown exception).

See the code on github.

See a demo.

Continue reading ‘New jQuery plugin, status’ »

I never really liked how my hotkeys plugin (that allows things like $('body').on('keydown', {keys: '^A'}, function() {alert ('Control-A!'); })) worked internally, and it didn't allow multiple handlers on a single element for a given key combination, and required initialization. I went back and looked at it some more, and studied John Resig's hotkeys, and came up with a simpler, better version. Use is largely the same, but there's no $.keymap.hotkeys('keydown') to initialize, and you have to explicitly remove handlers with $().off() rather than replacing with a new handler.

See the code.

This is obsolete. The documentation is now on github.bililite.com/timerevents.

Code and demo are on github.

I wanted to have a simple word count on the Kavanot editor, and this:

$('span.wordcount').text($('#editor').val().split(/\s+/).length+' words');

is enough for me. Keeping it live is straightforward:

$('#editor').on('input', function(){
  $('span.wordcount').text(this.value.split(/\s+/).length+' words');
});

But that won't show the word count when the page is first loaded. The ready event only works on document, and there isn't any event that just fires immediately (as far as I can see). So I created one.

$(selector).on('immediate', function() {...}); just calls the function with the relevant element as this. That's pretty much worthless, but you can combine the events:

$('#editor').on('immediate input', function(){
  $('span.wordcount').text(this.value.split(/\s+/).length+' words');
});

And now the word count shows immediately and is live, and remains DRY.

Continue reading ‘Timer events for jQuery’ »

Turns out Firefox uses a different keycode for the ;/: key from Chrome. I thought jQuery was supposed to normalize everything across browsers! Added the correct keycodes for that key to the keymap plugin. It's now at version 1.2.

Fixed a bug where the selection was not set correctly (a regression from when I updated bililiteRange). It's now at version 2.2.

This post is obsolete; the $.keymap has changed significantly. Documentation is now at http://bililite.com/blog/2015/01/12/rethinking-keymap/.

Inspired by John Resig's hotkeys, I created a version that uses the modern on, and uses an object rather than a string for the keys (which would be confused with a selector). It also uses my $.keymap codes.

It's written as a plugin to $.keymap.

Download the code.

See the demo, or the demo with regular expressions.

It's part of my bililiteRange repo on github.

Usage

This modifies the event handling code so that you can listen for specific keys or sequences of keys (note that you can use keyup as well as keydown, but that is less useful):

$('input').on('keydown', {keys: '^A'}, function() {alert('Control A was pressed')} );

The original $('input').on('keydown', function() {alert('A key was pressed')} ); is unchanged.

Remove the handler with $('input').off('keydown', {keys: '^A'} );. Use exactly the same keys expression as when the handler was attached.

It handles selectors correctly (I think):

$('body').on('keydown', {keys: '^A'}, function() {alert('Control A')} );
$('body').on('keydown', 'input', {keys: '^A'}, function() {alert('Control A was pressed in an input')} );

Attaches the handler for <body> and keys ^A, then adds a handler that is only triggered when the target element is an <input> and the event bubbled up to the <body>.

Remove selector-specific handlers with $('body').off('keydown', 'input', {keys: '^A'} );.

Note that the handler function is replaced internally, so removing handlers with $('body').off('keydown', handler); will fail. It's not a good idea anyway (see the documentation); use namespaces instead ($('body').off('keydown.myinstance');).

Sequences of keystrokes are indicated by a space-delimited list:

$('body').on('keydown', {
    keys: '{up} {up} {down} {down} {left} {right} {left} {right} b a'
  }, 
  function() {alert('Konami!')}
);

Listens for the Konami code.

Normally, when listening for a sequence, the keydown events that match are discarded. If you want them to be passed to other handlers, use allowDefault:

$('body').on('keydown', {
    keys: '{up} {up} {down} {down} {left} {right} {left} {right} b a',
    allowDefault: true // the arrow keys will still work
  }, 
  function() {alert('Konami!')}
);

To prevent other handling on the final event of a sequence, use the usual preventDefault or return false in the handler.

Regular Expressions

In addition to specifying the keys with a string, you can use a regular expression:

$('body').on('keydown', { keys: /\d/ }, 
  function(event) {alert('A digit key was pressed:' + event.hotkeys)}
);

The actual key or sequence of keys is returned in event.hotkeys. Note that passing the keys as a string will "normalize" the keys with $.keymap.normalize to match that returned by $.keymap (i.e. {keys: 'ctrl-a'} will work). Passing a regular expression needs to be normalized (i.e. {keys: /ctrl-\w/} will not work; use {keys: /\^\w/}). Multiple keys need to be separated by a single space ({keys: /\d \d/} to catch two digits in a row). This will succeed on the first sequence of characters matched, with no look-ahead, so you can't do {keys: /(\d )+/} to match multiple digits; it will succeed on the first digit pressed.

Events

The plugin also triggers two other events: 'keymapprefix' when the event is the prefix of a valid sequence, and 'keymapcomplete' when a sequence is matched. Both are sent with an additional parameter indicating what keys were pressed so far (in $.keymap notation). So:

$('body').on('keymapprefix keymapcomplete', function(event, keys){
	alert ('sequence pressed: '+keys);
})

More Information

Understanding special event handling in jQuery is very complicated; there is an article on learn.jquery.com that is invaluable. But this is one area where you will have to go through the source code. No way around that, but it's a very good way to get your brain about the way jQuery works, in a deep way.

Edited 2013-12-11 to use focusout rather than blur event

Continuing my adventures in developing an online editor, I wanted to be able to do "live" searching: having the text scroll to the sought string as it is being typed. I can use my bililiteRange utilities to find an arbitrary regular expression, but after much hair-pulling, realized that my texthighlight plugin is just too flaky and hack-dependent to be used reliably. So I went a different route: instead of trying to highlight strings in a <textarea>, I overlay a <pre> element desinged to look similar enough to the <textarea> and highlight the string in that with normal HTML. When the user is done typing his search string (signaled by a blur event on the <input> element being typed in), remove the <pre> and select the desired string in the original <textarea>. There's a jump but not as bad as with texthighlight.

Download the code.

See a simple demo, a demo that uses the parameters, and a demo that searches three elements at once.

Continue reading ‘New jQuery plugin, livesearch’ »