‘JavaScript’ Archive

Proof of Concept: Bindings in JavaScript

In another attempt to copy ideas from Actionscript 3 to JavaScript, I spent a few hours trying to implement bindings. I may try to implement an MVC framework like Cairngorm in JavaScript someday, and bindings play an important role in Flex and Actionscript.

Bindings keep UI elements in sync with data with minimal code. In Cairngorm, all application data lives in a ModelLocator singleton, so UI elements have a single point of reference for application data.

I’m sure there are many ways to implement binding in JavaScript, but I started with my ideal API:

  1. Binding.bind(object, "property", object2, "property2");

That method call does a few simple things:

  1. Adds generic getter and setter methods. The setter has a hook into the binding utility.
  2. If an object is an applicable HTML element, it adds the appropriate event listeners that hook into the binding utility.
  3. It stores the respective object/property pairs for each object.

This implementation forces you to use object.set("propertyName", value) in order to trigger binding. The binding trigger can easily be implemented in custom objects as well with this method call:

  1. Binding.trigger(object, "propertyName");

Where object is the bound object. This creates a cascade of calls setting the appropriate values on properties of other bound objects.

I avoided recursion by creating a unique transaction id every time Binding.trigger is called. The set method passes this id along internally so no object is changed twice in the same transaction.

So far I only support text and checkbox inputs, textareas, and select dropdowns. The selected option in a select dropdown doesn’t amount to a single property, but instead to element.options[element.selectedIndex].value. It was easy to override the get and set methods to allow Binding.trigger(element, "value").

See the example and check out the code. I used the Flow JavaScript library to normalize the DOM API (really just for addEventListener), but it doesn’t rely on any particular library.

Developing Silverlight 1.0 RIAs: JavaScript Optimization

(Read the intro here.)

My appreciation and respect for JavaScript increased tremendously while developing Silverlight projects. Not at all because of how easy it was to work with. But its flexibility and power made it interesting and fun to tackle challenges like optimization and application architecture.

Optimization

In Silverlight 1.0, the plugin is only a rendering and animation engine. All of the logic and much of the processing still resides in the browser’s JavaScript interpreter. All web applications need to make optimization a priority, but this is especially true for Silverlight 1.0 applications.

Doing a lot of small things can add up to make a big difference. Swap myArray.push(obj) with myArray[myArray.length] = obj. Avoid string concatenation when possible to give Internet Explorer a little boost.

I made two significant changes to my code between the WWE project and the last Library of Congress project that I believe made a big difference.

Avoid Function.bind

I usually resorted to a Function.bind method (usually the version in prototype.js) to manipulate function scope. This really comes in handy for event listeners:

  1. // inside of a class
  2. function addMouseEvents() {
  3.     this.xaml.addEventListener('MouseEnter', this.onMouseEnter.bind(this));
  4.     this.xaml.addEventListener('MouseLeave', this.onMouseLeave.bind(this));
  5.     this.xaml.addEventListener('MouseLeftButtonDown', this.onMouseDown.bind(this));
  6. }
  7.  
  8. function onMouseEnter(sender, mouseArgs) {
  9.     // "this" still refers to the instance of the class
  10. }

I really like this syntax, even though in an ideal world it wouldn’t be necessary. (Actionscript 3 is an ECMAScript implementation that fixes this). I’ve heard Mozilla might implement it natively, which would be lovely.

The problem with bind() is that it uses apply() internally, which is significantly slower than just executing the function. This makes a big difference for MouseMove or ProgressChanged event handlers.

Fortunately it is easy to avoid, and there are a number of ways to use depending on your personal preference. I usually used:

  1. // inside of a class
  2. function addMouseEvents() {
  3.     var that = this;
  4.     this.xaml.addEventListener('MouseEnter', function(sender, args) {
  5.         that.onMouseEvent(sender, args);
  6.     });
  7.     // .. other events
  8. }

I looked around for any documentation on the effects on bind() on performance and found nothing. This isn’t a Silverlight-specific issue but something other people might run into so it’s probably worth documenting.

Using and Removing Event Listeners

This also isn’t necessarily Silverlight-specific, but it’s also worth mentioning because of a few difference from how HTML DOM event listeners are handled. The crux of the matter is still that adding event listeners is memory intensive and not disposing of them properly can cause leaks and sluggishness.

In HTML you can use event delegation to limit the number of event listeners added. Add a single click event listener to a parent object, and act upon the event based on on the target property of the event, which is usually a child of the parent object. This isn’t possible in Silverlight 1.0 because there isn’t a real event object. The sender argument sent to the listener is always the object to which the event was added.

The only way to combat the overhead from adding event listeners is to reuse objects when possible. For WWE, there is a big grid of thumbnails the user can click on. Instead of creating a whole new grid each page refresh, I just changed the data for each item.

Sometimes reuse isn’t an option, and you’re forced to create and destroy XAML objects repeatedly. Removing event listeners before destroying an object isn’t difficult.

  1. var eventToken = xaml.addEventListener('MouseEnter', function() {});
  2. xaml.removeEventListener('MouseEnter', eventToken);

I came up with a way to store those event tokens in a simple, behind-the-scenes manner, which made this easier to do than not do. I’ll describe this in the next section on JavaScript/Silverlight Architecture.

Adding CSS rules with JavaScript to hide content on page load

Update: I’ve had to update the code to get it to work reliably in Safari 2. Much thanks to this article on the YUI Blog for another solution.

A lot of ajax-y web pages involve hiding and showing content. My current project has a tons of content related to a video in dynamic reveals that the user can open and close. Of course, accessibility is important, so for users without JavaScript enabled, all of this hidden content should be visible.

Typically, JavaScript would explicitly hide this content as soon as it is available. Something like:

  1. // using prototype.js
  2. document.observe("dom:loaded", function() {
  3.   // initially hide the reveal container
  4.   $('revealContent').hide();
  5. });

I chose prototype.js because of its notorious file size, and I know a lot of you still don’t compress and gzip their JavaScript! If you’re loading a lot of JavaScript, HTML, and CSS, chances are pretty likely that the #revealContent element will display before that that function has a chance to run.

So how do we hide that element before it has a chance to load? The technique I’m using adds a CSS rule to a new stylesheet as soon as possible, i.e. when the JavaScript is interpreted.

  1. /* Add a rule in CSS that lets you override the generated rule */
  2. #revealContent.show {
  3.     display: block;
  4. }
  1. // JavaScript
  2.  
  3. var CSSRules = function() {
  4.  
  5.  var headElement = document.getElementsByTagName("head")[0],
  6.   styleElement = document.createElement("style");
  7.  styleElement.type = "text/css";
  8.  headElement.appendChild(styleElement);
  9.  
  10.  // memoize the browser-dependent add function
  11.  var add = function() {
  12.   // IE doesn't allow you to append text nodes to <style> elements
  13.   if (styleElement.styleSheet) {
  14.    return function(selector, rule) {
  15.     if (styleElement.styleSheet.cssText == '') {
  16.      styleElement.styleSheet.cssText = '';
  17.     }
  18.     styleElement.styleSheet.cssText += selector + " { " + rule + " }";
  19.    }
  20.   } else {
  21.    return function(selector, rule) {
  22.     styleElement.appendChild(document.createTextNode(selector + " { " + rule + " }"));
  23.    }
  24.   }
  25.  }();
  26.  
  27.  return {
  28.   add : add
  29.  }
  30. }();
  31.  
  32. CSSRules.add('#revealContent', 'display: none !important');
  33.  
  34. // later
  35. $('revealContent').addClass('show');

I haven’t tested this exhaustively, but it works as intended in Internet Explorer 6 and 7, Firefox 2 and 3, Safari 2 and 3, and Opera 9. I originally expected some strange behavior from adding elements to the <head> before it finished loading, but fortunately this isn’t the case.

You could also include a stylesheet or a <style> tag in a <noscript> tag, but I prefer to keep all of this behavior in JavaScript.

Update: The old code:

  1. // JavaScript
  2. var CSSRules = function() {
  3.  
  4.     // create a stylesheet
  5.     var stylesheet = document.createElement("style");
  6.     stylesheet.setAttribute("type", "text/css");
  7.     document.getElementsByTagName("head")[0].appendChild(stylesheet);
  8.     stylesheet = document.styleSheets[document.styleSheets.length - 1];
  9.  
  10.     return {
  11.         add: function(selector, rule) {
  12.             // the Microsoft method
  13.             if (stylesheet.addRule) {
  14.                 stylesheet.addRule(selector, rule, 0);
  15.  
  16.             // the standard method
  17.             } else if (stylesheet.insertRule) {
  18.                 // safari 2 apparently ignores this
  19.                 stylesheet.insertRule(selector.concat('{' + rule + '}'), 0);
  20.             }
  21.         }
  22.     };
  23. }();
  24.  
  25. CSSRules.add('#revealContent', 'display: none !important');
  26.  
  27. // later
  28. $('revealContent').addClass('show');

How YUI’s event listeners changed the way I write JavaScript

I really like Prototype.js. Its inclusion in Ruby on Rails got me writing JavaScript again. We at Schematic don’t recommend Prototype for client projects however, and I’m glad because the library or framework you use can greatly change the style in which you write code. And sometimes that can make you a better programmer.

Working with YUI helped trigger an epiphany for me this past week. Credit for laying the foundation for this moment goes to the sample chapter on Inheritance of Douglas Crawford’s new book, JavaScript: The Good Parts. I did not understand the practical usage for the techniques in the chapter at first because I learned Object Oriented design in classical OO languages like Actionscript 2, PHP5, and Ruby (which Prototype imitates as best as possible).

I’ve tried to maintain this style in JavaScript, especially in large projects like Silverlight 1.0 RIAs. I usually structured classes like this:

  1. var MyClass = function() {
  2.     // MyClass constructor
  3. }
  4. MyClass.prototype ={
  5.     method : function() {
  6.         // do stuff with the "this" object
  7.     }
  8. }
  9.  
  10. var myInstance = new MyClass();

I eventually extracted the new Class.create function from Prototype.js 1.6.0, because it gives you an elegant syntax for inheritance, mixins, and access to overridden methods without much overhead. It basically does the same as the above with some extra magic for creating the inheritance chain.

There’s nothing inherently wrong with this approach, and I think that anyone who tells you otherwise is a bit of a zealot. But it’s reliance on the this object creates problems, especially with event listeners. YUI made this very apparent:

  1. var GUIElement = function() {
  2.     var button = document.getElementById('button');
  3.     YAHOO.util.Event.addListener(button, 'click', this.onButtonClick);
  4. }
  5. GUIElement.prototype = {
  6.     onButtonClick : function(event) {
  7.         // this === button
  8.     }
  9. }
  10.  
  11. var element = new GUIElement();

The scope of the event handler is the button, which I believe follows the standard. However, I usually want this to refer to the instance of the class so I can access other methods and properties. The bind method in Prototype makes this easy by creating an anonymous function that executes the function in the specified scope:

  1. button.observe('click', this.onButtonClick.bind(this));

There are a few problems with this approach.

  1. bind uses Function.apply which adds overhead. It’s usually negligible but with JavaScript optimization is always a top priority.
  2. We’re not using Prototype! In this case, the client is a large e-commerce site and we’re only coding a small section of it. Prototype doesn’t necessarily place nice with other libraries since it defines a lot of global variables and augments the prototypes of build-in objects (like adding bind to Function).

You can specify the scope of the event handler with YUI, but it has the same effects as the first point above.

  1. // a "truthy" last argument makes the second-to-last argument the function scope
  2. YAHOO.util.Event.addListener(button, 'click', this.onButtonClick, this, true);

This felt very awkward to me, like I was doing something I wasn’t supposed to do. It’s perfectly valid but the awkwardness got a few gears turning in my head. I don’t think I can adequately explain my train of thought without turning this into an unreadable ramble, but here is how I’ve started writing components (using the module pattern):

  1. var GUIElement = function() {
  2.  
  3.     // define constants, like shortcuts to the YUI library
  4.     var Event = YAHOO.util.Event;
  5.  
  6.     // this "constructor" creates an object and passes it to functions
  7.     // instead of calling methods on the object itself
  8.     function create() {
  9.         var object = {}; // the instance
  10.         object.button = document.getElementById('button');
  11.         addEvents(object); // pass it to the function
  12.         return object;
  13.     }
  14.  
  15.     // we'll call the object "that" so it kind of looks like "this"
  16.     function addEvents(that) {
  17.         // pass "that" to the event handler, but don't change scope
  18.         Event.addListener(that.button, 'click', onButtonClick, that);
  19.     }
  20.  
  21.     function onButtonClick(event, that) {
  22.         // do stuff with that
  23.         // as a bonus, "this" still refers to the button
  24.     }
  25.  
  26.     return {
  27.         create: create // make create a public function
  28.     }
  29. }();
  30.  
  31. // the module pattern means no more "new" keyword
  32. var element = GUIElement.create();

And all of a sudden, Crockford’s techniques make sense! My paradigm shift is really just two ideas:

  • Encapsulate code into factories, not classes.
  • Functions act upon objects instead of being methods of the object that act upon itself (addEvents(that) instead of this.addEvents()).

I’ve yet to fully explore the ramifications of these techniques, but I wanted to try writing this down to see if it still makes sense. I won’t throw Class.create out of my toolbox just yet, but if I can understand an elegant way to do inheritance–or more likely, composition–I do think this will be the way to go 90% of the time. More on this topic to come.

Addendum: Curiously, Luke W posted an article while I wrote this about the exact component I was refactoring when this epiphany hit me.