Screw.Unit tests not displaying properly in IE

The Screw.Unit JavaScript BDD framework is awesome, I love it.  Unfortunately if you grab the current head from github, it comes down with jQuery 1.2.6.  Using 1.2.6 with Screw.Unit and trying to run your tests in IE will result in something like this:

image

when it should look like this:

image

Simply upgrade from jQuery 1.2.6 to 1.4.1 and the problem will go away:

image


Posted by: Jeff
Posted on: 2/19/2010 at 8:07 PM
Categories: jQuery | Testing
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (1) | Post RSSRSS comment feed

Being careful with jQuery's bind() and introducing bindOnce

jQuery's bind() method is extremely useful. If you call $(myElement).bind('change',function(){...}) multiple times, you can chain multiple events on the same element.

$('a').bind("click", function() { alert('event 1'); });

$('a').bind("click", function() { alert('event 2'); });

These two calls will take all links on in your DOM and alert("event 1") and alert("event 2") when any of them are clicked. Fantastic!

However, there is the potential for an undesired side effect if you use bind unwisely.  For example, say you have a modal dialog popup in your application.  When you have to display the modal dialog, you perform some setup first:

   1:  function SetupModalDialog() {
   2:      $('select', '#dialogContainer').bind('change',function(){
   3:          alert('be careful what you wish for');
   4:      });
   5:  }
 

If for some reason you have to call SetupModalDialog multiple times (perhaps because some of the controls are dynamic based on other parts of the page), the drop downs will have this function bound to the change event each time this function is called.  What does this mean?  If you open your dialog twice, and change the selection on a dropdown in the dialog, you will see two alert windows saying "be careful what you wish for".

There are a couple of ways you can handle this situation. One would be to go back through the elements you've setup events on and call unbind. Having clean up code like this is ideal, but not always practical. Another solution which will minimize your code changes is to make sure you're not binding the same function more than once.

Introducing bindOnce

   1:  function bindOnce(element, event, handler) {
   2:      if (element === undefined || event === undefined || handler === undefined)
   3:          return;
   4:          
   5:      var jqueryObj = $(element);
   6:   
   7:      var events = jqueryObj.data('events');
   8:   
   9:      if (events === undefined) {
  10:          jqueryObj.bind(event, handler);
  11:      }
  12:      else {
  13:          if (events[event] === undefined) {
  14:              jqueryObj.bind(event, handler);
  15:          }
  16:          else {
  17:              var isBound = false;
  18:              $.each(events[event], function(i, eventHandler) {
  19:                  if (eventHandler.toString() === handler.toString()) {
  20:                      isBound = true;
  21:                      return false;
  22:                  }
  23:              });
  24:   
  25:              if (isBound === false) {
  26:                  jqueryObj.bind(event, handler);
  27:              }
  28:          }
  29:      }
  30:  }

bindOnce simply excepts and element, the event to handle ('click'), and the function to handle the event.  By using the jQuery.data("events") collection, we can determine if the event and function have already been bound to the element in question.

Of course what good reason is there to have a method to do the work that does not extend jQuery? None that I can think of, so here's the jQuery plugin:

(function($) {
    $.fn.bindOnce = function(event, handler) {
        if (event === undefined || handler === undefined)
            return;
 
        var events = this.data('events');
 
        if (events === undefined) {
            this.bind(event, handler);
        }
        else {
            if (events[event] === undefined) {
                this.bind(event, handler);
            }
            else {
                var isBound = false;
                $.each(events[event], function(i, eventHandler) {
                    if (eventHandler.toString() === handler.toString()) {
                        isBound = true;
                        return false;
                    }
                });
 
                if (isBound === false) {
                    this.bind(event, handler);
                }
            }
        }
    };
})(jQuery);

The usage is typical:

   1:  $('a').bindOnce('click', function() { alert('Hey...'); });

Enjoy.

Posted by: Jeff
Posted on: 1/12/2010 at 11:05 PM
Categories: jQuery
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed