dans.blog


The miscellaneous ramblings and thoughts of Dan G. Switzer, II

jNotify jQuery Plug-in v1.2.00

The jNotify plugin was just updated, with a new "click to dismiss" feature, which lets a user click on a notification to dismiss the notification!


MouseIntent jQuery Plug-in

One common pattern I've seen in usability, is that users don't always have great control over their mouse. It's easy to accidentally overshoot a target area with you mouse, which is really frustrating with things like nested dropdown menus, where accidentally mousing away from a target area may end up collapsing/closing the open menu.

Giva's recently released the MouseIntent jQuery Plug-in which aims to give developers a way to control this behavior. It works by monitoring an invisible border around the element to see what the user appears to be doing. If a user quickly moves back into the original element, then then no mouseaway event is ever fired. The plugin has a number of settings that allow you to control the behavior—such as monitoring whether or not the user is still moving the mouse in the invisible border area.

I've used the plugin in one of our dropdown menus that has nested menus and it's really helped to improve our user's experience.


Maskerade Date Mask Input Plugin

Giva has released released a new plugin (Maskerade jQuery Plug-in) which can convert a normal text field into a power date mask input field. The plugin supports a large array of date masks (even quarters) and even supports copy/paste. Here's a list of some of it's key features:

  • Keypress validation (ie. you don't need to submit the form for the mask to be applied)
  • Full keyboard support, including number to text-date interpretation (eg. typing 6 for a month will show June) and number-entry interpretation (eg. typing 02 in a yyyy date field will be interpreted as 2002)
  • Full mouse support
  • Masks can be defined as attributes of the input field; individual jQuery mask calls are not needed
  • Includes time-mask capability, with a date or alone
  • Default values and masks set as placeholders in the input field
  • Ability to set min and max dates allowed on a field
  • Allows for enforcing relational validation (ie. date1 must be before date2)
  • Automatic adjusting for invalid dates (eg. Feb 29, 2001 is adjusted to Feb 28, 2001)
  • Each date/time part fully highlighted on focus
  • Automatic tabbing to next date/time part once interpreted (eg. typing 2 in a "mm" date part will automatically tab you to the next date part), which allows quick keyboard entry
  • Allows for dask masks by quarters (eg. Q1, Q2, etc.)
  • Ability to support multiple languages
  • Custom event handlers; for example, a single keystroke can be defined to change the date to the current date
  • Detach/attach Maskerade behavior from the element

We have actually been using this plugin in production for a long time with great success.


Update to the Linkselect jQuery Plugin (v1.5.11)

I just pushed out an update to the Linkselect jQuery Plugin.

Revisions

v1.5.11 (2013-07-09)

  • Linkselect plugin now annouces an "update" event whenever the value changes--which allows you to set up listeners for when the value has changed.


Update to the mcDropdown jQuery Plugin (v1.3.3)

I just pushed out an update to the mcDropdown jQuery Plugin.

Revisions

v1.3.3 (2013-07-09)

  • Fixed issue where menu option underneath a selected sub-menu option would sometimes cause the option to disappear from the menu
  • Fixed issue where sub-menus would sometimes still be open after re-opening a menu


Adding custom callbacks to existing JavaScript functions

This morning I was reading Adding your own callbacks to existing JavaScript functions by Dave Ward—which covers how to overwrite an existing function so you can add some additional functionality (in this case, adding callbacks.) While the article is informative, a couple of improvements can dramatically improve his suggestion.

If you don't want to take the time to read Dave's article, in a nutshell he describes how we can overwrite a JavaScript function by storing a reference to the original function in a variable. So, we can take the following function:

function sayHello(name){
  alert("Hello, " + name + "!");
}

And we can now overwrite that function by storing a reference to the original function in a variable:

var sayHelloOld = sayHello;

function sayHello(){
  var name = prompt("Enter your name");
  sayHelloOld.apply(this, [name]);
}

Now there's a couple of problems with the above code.

  1. As written, this would only work if the original sayHello() function was defined in another <script> tag because of function hoisting.
  2. We're polluting the global name space.

We can solve both those problems by using a closure around our code:

// define a closure and pass in a reference to the global window object
(function (w){
  var sayHelloOld = w.sayHello;

  w.sayHello = function (){
    var name = prompt("Enter your name");
    sayHelloOld.apply(this, [name]);
  }
})(window || {});

(NOTE: You can see a working copy on JSFiddle.)

The other topic Dave discusses is how to add callback hooks to run before and after a the original function code runs. His suggestion is built around using the global name space to declare some function names. Since the example is based around jQuery, I'd suggest a much better method would be to add in custom events to your function. This gives you a way to bind callbacks to run, but your neither cluttering the global namespace nor running into issues if the callbacks aren't needed.

Dave's Original Solution

Here's what Dave's original solution looks like:

var oldTmpl = jQuery.fn.tmpl;

// Note: the parameters don't need to be named the same as in the
//  original. This could just as well be function(a, b, c).
jQuery.fn.tmpl = function() {
  if (typeof onBeforeTmpl === 'function')
    onBeforeTmpl.apply(this, arguments);

  // Make a call to the old tmpl() function, maintaining the value 
  //  of "this" and its expected function arguments.
  var tmplResult = oldTmpl.apply(this, arguments);

  if (typeof onAfterTmpl === 'function')
    onAfterTmpl.apply(this, arguments);

  // Returning the result of tmpl() back so that it's actually 
  //  useful, but also to preserve jQuery's chaining.
  return tmplResult;
};

Improved Solution

Using the two previously mentioned techniques combined, here's how I'd change that code:

(function ($){
  var oldTmpl = $.fn.tmpl;

  // Note: the parameters don't need to be named the same as in the
  //  original. This could just as well be function(a, b, c).
  $.fn.tmpl = function(){
    // trigger the before callback
    // to attach a callback, we just bind() this custom event to our jQuery object
    this.trigger("onBeforeTmpl", arguments);

    // Make a call to the old tmpl() function, maintaining the value 
    //  of "this" and its expected function arguments.
    var tmplResult = oldTmpl.apply(this, arguments);

    // trigger the after callback
    // to attach a callback, we just bind() this custom event to our jQuery object
    this.trigger("onAfterTmpl", arguments);

    // Returning the result of tmpl() back so that it's actually 
    //  useful, but also to preserve jQuery's chaining.
    return tmplResult;
  };
})(jQuery || {});

This gives use a few benefits over Dave's original solution:

  1. We're not polluting the global namespace
  2. We can now attach custom callbacks to each jQuery object separately

To use our code, we can do:

$("#id")
  .bind("onBeforeTmpl", function (){
    alert("before!");
  })
  .bind("onAfterTmpl", function (){
    alert("after!");
  })
  .tmpl(data, options, parentItem);
NOTE:
If your prefer to run the same callbacks for all $.tmpl() calls, you could attach the custom events globally.

Any comments on how to make this solution even better?


jQuery Linkselect Plug-in v1.5 released!

Today a new version of the jQuery Linkselect Plug-in was released. The new version under went a ton of changes from the previous version and includes a much better CSS skinning mechanism (see the Linkselect Example.)

The coolest new feature is probably the "placeholder" support, which allows you to specify an <option /> element in your <select /> box to use as a label/placeholder. The "placeholder" <option /> tag uses the <option /> tag as the title of the dropdown menu that's always visible, but still selectable. Here's an example:

Here's a list of all the changes:

  • Revised CSS naming structure to make it to have multiple visual styles of linkselect elements on the same page. The changes in v1.5 are not backwards compatible, so you will need to update your CSS in order to upgrage to v1.5. You can either replace you existing CSS files with one included in the zip file, or you can modify your existing CSS files to match the new naming syntax (which for the most part is just changing the camel casing from "linkselectLink" to "linkselect-link", "linkselectLinkOpen" to "linkselect-link-open", etc. Also, all CSS classes now using the prefix "linkselect-" by default.
  • Added new "style" option. This replaces all the previous class* options. The default style is "linkselect" and is simply used as the prefix used for all the CSS classes. You can create custom linkselect skins by just copying one of the included CSS files and changing the prefix to match your style option value.
  • Added "text" API call to return the label of the currently selected option.
  • Added "destroy" API call to destroy a linkselect element and return to original select element.
  • Changed the "replaceOptions" API to add a new "includeFilter" argument. The API change is backwards compatible.
  • jQuery.delegate() is now used to monitor event changes
  • Changed <li /> elements to use "data-" attributes for custom attributes
  • Changed <li /> elements to use "data-value" for storing selectable value instead of the "rel" attribute.
  • Fixed issue with Chrome not correctly accepting focus when the linkselect element was clicked.
  • Events related to closing/resizing of the open linkselect element are now registered/unregistered when the menu opens. This helps improve performance when there are many linkselect elements on the page as it reduces the document.click events that fire to see if the menu should be closed.
  • Added ability to use both "name" and "id" attributes on a select element with different values.
  • Added new "placeholder" functionality. You can now give an option element a class of "placeholder" to have it used as a selectable title for the linkselect element. This allows you to use a "Please select..." option element in your select and have it used as the title of the linkselect. Users will be able to select this option, just like any other option element.
  • Fixed various positioning bugs.


Using simple conditional logic in JsRender

I've been updating some code that was built using jQuery Templates, but since jQuery Templates has been discontinued, I've decided to migrate my code to use JsRender.

The syntax is close enough for the most part that translating existing templates is straightforward enough. For the most part it's updating to the new mustache-like bracket syntax and changing variables from ${var} to {{=var}}. However, there was one thing that was tripping me up—simple conditional statements.

JsRender uses the mustache like "codeless" syntax for conditions:

{{#if varName}}output{{/if}}

This fine if varName is a truthy statement, but what if you need to check if varName  is equal to a specific value? Having gone through all the JsRender sample code, it appeared the only way to accomplish any kind of comparison conditional statement was to use custom code. This syntax ends up being very ugly and verbose. Here's an example of writing an attribute dynamically based on the value of a variable named varName :

<div class="base{{* if($view.data.varName == 'None'){ result += ' none';  } }}">{{=varName}}</div>

This seems overly complicated for such a simple comparison. I figured there had to be a better way.

After digging through the code, I found there's some undocumented features of the if tag. You can actually use the following syntax to do the same thing:

<div class="base{{#if varName eq='None')}} none{{/if}}">{{=varName}}</div>

While this syntax ends up being pretty close to jQuery Templates, it uses the "hash" functionality of JsRender's custom tags. The odd looking part is that you need the equal sign between the conditional operator and the value. Other operators include:

  • eq = equals
  • ne = not equals
  • lt = less than
  • le = less than or equal
  • gt = greater than
  • ge = greater than or equal

I actually would prefer to see "lte" and "gte" used for the "or equal" functions, so I've added that to a ticket.

So, while the syntax isn't ideal, it's certainly better than using using the code syntax. Since the project is still in development, it possible the syntax will change.


Issues with the throttle() function in Underscore.js and my throttle() fixes

[UPDATED: Wednesday, August 24, 2011 at 3:19:27 PM]

I was looking through the source code of Underscore.js this morning and notice it's implementation of _.throttle() contains two issues that bother me with most JavaScript-based throttle implementations I've seen:

  1. It delays the execution of the first hit until the "delay" has been reached. While this is ideal for debounce type operations, I believe throttle operations should execute immediately and then only execute after the delay for each additional call. Throttling works really well for mouse movement and scrolling operations, but you usually want the behavior to initiate when the operations begins—delaying the first execution until the delay is reach usually ends up with an odd behavior.
  2. The arguments passed to the throttled event are based upon the invoking function that triggered the setTimeout() event. This means you are not dealing with the latest data passed to your throttled event, but dealing with expired data. A perfect example of this is using throttling to monitor mouse movements. The way Underscore.js has implemented the throttle() function, you could end up with coordinates based on where the pointer started—not where it ended.

Let's take a look at an example I posted on JSFiddle: http://jsfiddle.net/MNGpr/

Mouse your mouse from the top left of the "Results" frame to the bottom right. If you do this quickly (under the 1 second delay,) you'll notice that when the function executes it's based upon coordinates in the upper left of the screen—not where your cursor left off.

What I've been doing is using the following throttle() function in my code:

// limit a function to only firing once every XX ms
var throttle = function (fn, delay, trail){
  delay || (delay = 100);
  var last = 0, timeout, args, context, offset = (trail === false) ? 0 : delay;
  return function (){
    // we subtract the delay to prevent double executions
    var now = +new Date, elapsed = (now - last - offset);
    args=arguments, context=this;

    function exec(){
      // remove any existing delayed execution
      timeout && (timeout = clearTimeout(timeout));
      fn.apply(context, args);
      last = now;
    }

    // execute the function now
    if( elapsed > delay ) exec();
    // add delayed execution (this could execute a few ms later than the delay)
    else if( !timeout && trail !== false ) timeout = setTimeout(exec, delay);
  };
};

What this version does that differentiates it from the Underscore.js version is it:

  1. Executes immediately upon first hit and then only after the specified delay. IMO, this provides a more desired effect for most throttled events. If you're really looking to do something when a user stops interacting with the screen, then use a debounce technique.
  2. The last arguments are applied when executing the throttled function. This means that the latest version of the values are used, instead of the values supplied to the initial timed event.

To see the difference in behavior, check out the JSFiddle example using my version of throttle().


jQuery Field, mcDropdown and iButton Plugins Updated!

I'm working on migrating our application to use the latest version of jQuery. In the process of migrating, I've been working on fixing compatibility issues with several of the plug-ins I've worked on to make sure they work with the latest version of jQuery. So, here's a list of updated plug-ins and their release notes:

Field Plug-in v0.9.4

  • Fixed issues with jQuery v1.6.x
  • Fixed file fields not currently supported by getValue or getType
  • Fixed formHash breaks on fields with apostrophes
  • Fixed moveNext() did not work, it have invisible parent

mcDropdown jQuery Plug-in v1.3.1

  • Fixed support for jQuery v1.6.2

iButton jQuery Plug-in v1.0.03

  • Fixed compatibility issues with jQuery v1.6.2
  • Added className option for adding an additional class name to the main container—which is useful for adding alternative styles to the button
  • Revised image sprite to be better organized (handles are now grouped together)
  • Updated CSS to reflect changes to image sprite
  • Fixed bug where iButton behavior could be attached multiple times if code attempts to re-initialize on the same element


Easy AJAX using ColdFusion, jQuery and CFCs

A recent post on CF-Talk asked whether ColdFusion could use AJAX to do a database lookup. This is actually extremely easy to do in ColdFusion 8+, because it natively supports returning data in the JSON format.

To show how easy this is to do, I decided to throw together a little demo. This took me about 10 minutes to write—most of which was writing the markup. However, the bulk of the work is going to be handled automatically by ColdFusion, which will handle converting your data to JSON and by the jQuery Field Plug-in (which I wrote) which will handle populating your form from the data it receives from ColdFusion.

To show just how easy this all is, here's the jQuery code required to make an AJAX call to a CFC:

$.ajax({
  // the location of the CFC to run
    url: "example.cfc"
  // send a GET HTTP operation
  , type: "get"
  // tell jQuery we're getting JSON back
  , dataType: "json"
  // send the data to the CFC
  , data: {
    // the method in the CFC to run
      method: "getUserById"
    /*
      send other arguments to CFC
    */
    // send the ID entered by the user
    , userId: $("#userId").val()
  }
  // this gets the data returned on success
  , success: function (data){
    // this uses the "jquery.field.min.js" library to easily populate your form with the data from the server
    $("#frmMain").formHash(data);
  }
  // this runs if an error
  , error: function (xhr, textStatus, errorThrown){
    // show error
    alert(errorThrown);
  }
});

Our CFC looks like this:

<cfcomponent output="false">
  <cffunction name="getUserById" access="remote" returnType="struct" returnFormat="json" output="false">
    <cfargument name="userId" type="numeric" required="false" />

    <cfset var user = structNew() />
    <!---//
      The only tricky part here is to use the bracketed notation
      to match the case in your HTML, JS is case sensentive.
      If you use the dot notation (user.name) then the keys will
      be returned in uppercase.
    //--->
    <cfset user["name"] = "User " & arguments.userId />
    <cfset user["email"] = "user_" & arguments.userId & "@example.com" />
    <cfif (arguments.userId mod 2) eq 0>
      <cfset user["gender"] = "f" />
    <cfelse>
      <cfset user["gender"] = "m" />
    </cfif>

    <cfreturn user />
  </cffunction>
</cfcomponent>

I've posted a working example so that you can see how this code looks. For the "User ID" just enter any number. If an error occurs, the error callback will handle displaying the error to the screen. I've also posted the source code as a zip file you can download.

NOTE:
If you're still on ColdFusion MX & 7, there's a little more work because non-string data automatically gets converted to WDDX. While there are JS libraries for converting WDDX to native JS objects, they can be hard to find now that the OpenWDDX site has been shut down. You can find tools at RIAForge though that can convert your data into a JS string--which jQuery will automatically evaluate when it's received.
UPDATE:
I've added some sample code to the download to show how you can use a proxy template in you're using ColdFusion 7 to get the same results. Just look at the example_cf7.cfm. The only difference is we call the example_cf7_proxy.cfm template instead of calling the example.cfc directly. The proxy template uses CFJSON to convert the results to JSON.

[UPDATED: Friday, March 04, 2011 at 9:59:11 AM]


Excluding a folder or file from being searched in Eclipse

The code base I work in every day includes a few external development tools that are included in our source code. We include the tools in the source code, because it's the easiest way to ensure that every developer has access to the tool and can run them quickly.

However, one disadvantage to include external development tools into your Eclipse projects is they can slow down searching your enclosing projects because they include files you generally don't really want to search. If you have a library such as "MXUnit" in your source, that adds hundreds of files that would be added to the search which you don't want.

One way you can avoid searching specific folders is to set up a "Workspace" and stick to searching workspaces. This works great, but requires that you maintain your workspace and keep it up to date. Our code base has thousands of files and hundreds of folders over several different projects, so I just find maintaining the workspace a little more tedious than I'd like.

I much prefer using the "Enclosing projects" option in Eclipse, because it searches the files in your active project. I find these is much easier than changing the workspace when I'm looking at files in a different project. So I did a little research this morning to see if there was an option to exclude folders/files from showing up in the search by default.

Turns out Eclipse has a feature called Derived resources:

Many resources get created in the course of translating, compiling, copying, or otherwise processing files that the user creates and edits.  Derived resources are resources that are not original data, and can be recreated from their source files.  It is common for derived files to be excluded from certain kinds of processing.

When you mark a folder or file as derived, it's excluded searches. The caveat is Eclipse assumes derived resources are probably compiled resources, so if you try to edit a derived file you will be warned that the file is derived. However, since these are external tools that I'm not needing to regularly update, this behavior is fine—and actually a nice reminder.

To set a folder as derived:

  1. Right-click on the file/folder
  2. Click on the "Properties" option
  3. Select the "Resource" option
  4. Place a check next to the "Derived" attribute

Eclipse Resource Screenshot

This has done the job nicely. I've been able to exclude several hundred files from showing up in my searches that I really don't care about!

NOTE: Generally derived resources are typically not files you'd keep in a repository, because they can easily be rebuilt from source code. While I had no problems setting a folder already in my SVN to derived, it's possible some source control plug-ins might treat a derived resource as being "ignored".


jNotify – A Light-weight Notification System for jQuery

One thing that's essential for any web application to be successful, is to implement a method for providing users with feedback based on their input. People want feedback based on their actions in real time. Users want to know that their actions had the intended consequence and if not, why it failed.

In the golden age of web applications, most applications solved this problem by returning a completely new page to the user any time they took an action on your website. If a user filled out a form on your website, the form's data would be sent to the server and the server would then generate a completely new page that either told the user that their action was successful or would show them the errors that occurred.

As web applications began to advance, more applications began to rely on JavaScript to provide more instantaneous feedback to users. If a user would leave a required field empty, developers would use a JavaScript alert() functions to notify users that they left the field incomplete. Over time, more and more applications have come to rely on JavaScript to provide users with a better user experience. The problem is that while the browsers have made many great strides over the years, we're still left with these very rudimentary UI controls for providing native feedback with users.

The alert(), prompt() and confirm() functions basically haven't changed at all since they were introduced. They offer essentially no control over the presentation and they're completely obtrusive to the user's experience. They all halt execution and force user action before any more processing occurs. While there are certain times this behavior is desired, there are many times when it's less than ideal.

For example, let's say you have you have a page that allows users to update bulk records in real time. On each row there is an "Update" button that allows a user to save updates back to the server. You obviously want to provide users with feedback after each update operation has completed. A quick solution is to use the alert() function when the update is successful.

The problems with this method are:

  • It's obtrusive—every time I display the "success" message to the user, I'm required to interact w/the alert box. This can really slow down the user's workflow.
  • No design control—I have no control over how the alert() dialog looks to the user. There's no difference between a successful message and an error message.
  • Only one message at at a time—I can never display multiple alerts to the user at the same time.
  • The alert() halts all activity—including any background tasks.

What we need is a better notification system that's just as easy to use, but allows us greater control over how the messages are display. This is where the jNotify Plug-in comes in. At just about 3KB, it's a very light-weight solution that gives us lots of flexibility over how we display notifications to the user. It allows us:

  • To display multiple messages to the user at one time
  • Use an HTML in the notification
  • Specify whether a notification is "sticky" or not (sticky messages require the user's interaction to close them)
  • Displays notifications in an unobtrusive manor—messages appear on the screen for short time, then fade away
  • Complete control over the positioning and appearance of the notification

I put together a short little video that illustrates some of the typical UI problems that the use of the alert() function introduces and show how jNotify can be used to solve those problems.

The jNotify Plug-in for jQuery

Implementing jNotify on your site is extremely easy. Just include the jquery.jnotify.js and jquery.notify.css files on your page, then substitute your use of alert() with $.jnotify().

So, instead of:
alert("An e-mail notification has been sent!");

You'd use:
$.jnotify("An e-mail notification has been sent!");

We've been using this plug-in in our own applications for over a year and we've had great success with it—especially when used with conjunction of providing feedback to users when AJAX operations complete.

You can see a live example on the jNotify jQuery Plug-in page.


Attaching mouse events to a disabled input element

I was doing some UI work this morning and ran into a bit of a problem. We have some HTML input elements that need to be disabled so the user can not interact with them. The problem is, we wanted to display a message to the user if they tried to interact with the element. While some browsers respond to a mousedown event, Firefox and Opera ignore all mouse-related events on a disabled form field. Since they seem to completely ignore mouse interaction, that means not even event delegation works.

After thinking about various ways to work around the behavior, I thought the most elegant solution might be to add an overlay over the disabled element which I'd use as the hotspot for mouse interaction. The overlay lays on top of the element and intercepts all mouse interaction.

Let's look at some sample HTML:

<label for="normal">
  <input type="checkbox" disabled="disabled" />
  Disabled Option
</label>

With a little jQuery magic, we can find the disabled element, get the parent <label /> element and then place a <div /> element over where the label element resides. The code looks like this:

// on DOM ready
$(document).ready(function (){
  // attach a click behavior to all checkboxes
  $(":checkbox").click(function (){
    alert("Clicked!");
  });

  // find the disabled elements
  var $disabled = $("#overlay-example input:disabled");

  // loop through each of the disable elements and create an overlay
  $disabled.each(function (){
    // get the disabled element
    var $self = $(this)
      // get it's parent label element
      , $parent = $self.closest("label")
      // create an overlay
      , $overlay = $("<div />");

    // style the overlay
    $overlay.css({
      // position the overlay in the same real estate as the original parent element 
        position: "absolute"
      , top: $parent.position().top
      , left: $parent.position().left
      , width: $parent.outerWidth()
      , height: $parent.outerHeight()
      , zIndex: 10000
      // IE needs a color in order for the layer to respond to mouse events
      , backgroundColor: "#fff"
      // set the opacity to 0, so the element is transparent
      , opacity: 0
    })
    // attach the click behavior
    .click(function (){
      // trigger the original event handler
      return $self.trigger("click");
    });

    // add the overlay to the page  
    $parent.append($overlay);
  });
});

You can see a live example here.


Rounding to the nearest fraction (i.e. specific decimal place)

I'm working on some "star rating" code and I really wanted to round numbers of to the nearest fraction. While the math is really basic, my math skills are rusty and this took me longer to solve than I care to admit. So I thought I'd just blog the solution for anyone else that might find it useful:

round( value/fraction ) * fraction

Like I said, it's very basic math but is very handy when you want to round to something other than to the closest integer.