Blog Customizations, Part 3: OO JavaScript

Not satisfied by the search feature provided by Community Server I’ve again created some helpful JavaScript classes this time using object-oriented JavaScript to define a basic search provider and to subclass that for specialized providers like MSN Search.

I defined several private properties and several privileged methods that can access those private properties, unlike public methods in classical object-oriented languages. Thanks to closures those methods can access those private properties — defined as local variables — even after the constructor function has returned.

function SearchProvider(title, home, image, url) {
  // Private properties.
  var _title = title;
  var _home  = home;
  var _image = image;
  var _url   = url;
 
  // Privileged methods.
  this.getTitle = function() { return _title; }
  this.getHome  = function() { return _home; }
  this.getImage = function() { return _image; }
  this.getUrl   = function() { return _url; }
 
  this.getHomeHTML = function() {
    return '<a href="' + _home + '">' + _title + '</a>';
  }
 
  this.getImageHTML = function() {
    return '<img src="' + _image + '" alt="' + _title + '" />';
  }
 
  this.getLogoHTML = function() {
    return '<a href="' + _home + '">' + this.getImageHTML() + '</a>';
  }
 
  this.search = function(keywords) {
    location.href = _url + escape(keywords);
  }
}
 

To define a subclass we use the base class to the prototype property of the subclass. We must also assign a new function to the constructor otherwise when the subclass is instantiated the base class constructor is called.

// Specialized SearchProvider for MSN Search.
MsnSearchProvider.prototype = new SearchProvider();
MsnSearchProvider.prototype.constructor = MsnSearchProvider;
function MsnSearchProvider() {
  // Call base class constructor.
  SearchProvider.prototype.constructor.call(this,
    "MSN Search",
    "http://search.msn.com/",
    "http://search.msn.com/s/affillogo.gif",
    "http://search.msn.com/results.aspx?q=site%3Ablogs.msdn.com%2Fheaths+");
}

The constructor now creates an instance of the specialized MsnSearchProvider and to call the base class constructor by calling the call method on the constructor. All that’s left to do is to replace property values and event handlers for the search-related elements. For this another class is created.

function Search(inputId, aId) {
    // Private properties.
    var _inputElem = document.getElementById(inputId);
    var _aElem = document.getElementById(aId);
    if (!_inputElem || !_aElem) {
        return null;
    }
  
    // Close 'this'.
    var self = this;
  
    // Public properties.
    this.provider = new MsnSearchProvider();
  
    // Change key handler for input.
    _inputElem.onkeydown = function() {
        if (event.keyCode == 13) {
            event.cancelBubble = true;
            event.returnValue = false;
            self.provider.search(_inputElem.value);
        }
    }
  
    // Change the href and add an onclick handler for the link.
    _aElem.href = "#";
    _aElem.onclick = function() {
        self.provider.search(_inputElem.value);
    }
  
    // Add the provider name under the intput.
    var _parent = _inputElem.parentNode;
    if (_parent) {
        var _divElem = document.createElement("div");
        _parent.appendChild(_divElem);
        _divElem.className = "provider";
        _divElem.innerHTML = "By " + this.provider.getHomeHTML();
    }
}

The this object must be closed so that the event handlers can access it; otherwise, when the event handler was called this would refer to the element on which the event was fired. Simply pass in the element IDs of the search textbox and link to perform the search.

While object-oriented JavaScript uses prototypal inheritance instead of classical inheritance, it is still a powerful tool. You can find lots of information about JavaScript inheritance and JavaScript closures that can help you learn more about object-oriented JavaScript.