DHTML Interfaces: Taking The Next Step

DHTML Interfaces: Taking The Next Step

any DHTML developers are tired of building complex interfaces around poorly implemented browsers only to have to reinvent the wheel on the next project. Many more are beginning to understand that maintaining interface state on the server is a very bad option from a usability standpoint. This article should impart some hope to the DHTML development community that we need not abandon DHTML to build robust, usable, and compelling interfaces.

That said, DHTML developers must develop a better toolset if they are to build dynamic Web-application interfaces that don’t require hundreds (or thousands) of man-hours of custom coding. In this article, I propose a basic starting point for defining a framework that allows better code reuse across projects and application interfaces. This framework will provide basic component lookup and creation services, and will allow developers to create components and widgets that are truly generic. In other words, the components generated by this framework are not tied to any specific environment or data set; they can be dropped in anywhere you need a widget of a given type. You can then simply tie them to the calculations or processes that they must support without the need to worry about how the components are constructed. In effect, this framework provides DHTML developers with a separation between data and presentation logic similar to that available to “regular” HTML developers via CSS and XHTML.

You can see a simple demonstration of the framework outlined in this article in the test_page.html file. The sample will not work on 4.x browsers, however the sample code for this article has been tested to work on a multitude of platforms, and should perform correctly on any modern browser, such as Internet Explorer (IE) version 5 or higher on both the Mac and PC platforms, Mozilla, Netscape Navigator (NN) 6, and Konqueror. The example is simplistic but serves adequately to illustrate the scripts that make it work and for examining why and how a framework can help you construct arbitrarily complex interfaces on the client side with minimal recurring effort.The Example Page, Step-by-Step
Now that you’ve tried the sample page, I’ll explain why it’s special. The first thing to note is the relative simplicity of the contents of the tag:

         

click anywhere in the document to spawn a new button widget

Despite the fact that the page consists of nothing more than a single headline element and a function call, it’s able to spawn and track an arbitrary number of button widgets. The sample page proves:

  • that you can create widgets after the page has fully loaded
  • widget creation is not dependent data sent from the server nor on initialization variables
  • widget creation can be fully event driven

The addButton() function does the heavy lifting. Stripped of comments and extraneous code, the function looks like this:

    function addButton(){      var tmpButton = new buttonWidget('button', null,          "alert('button ' + this.IDNum + ' clicked');");      tmpButton.generate();      document.body.appendChild(tmpButton.compNode);      document.body.appendChild(document.createElement("br"));   }

Four lines to spawn a widget, give it attributes, attach an event handler, add it to the document and add spacing isn’t bad?and that’s all end developers normally ever need to see or know about. The object constructor new buttonWidget() calls a constructor included from another script. You need to understand the constructor’s arguments to make full use of the widget.

The first argument specifies the text or “label” for the new button widget. The second and third (both optional) arguments provide methods for defining the behavior of the widget. The second argument accepts a function pointer to a method to be evaluated when the button is clicked, or null if it is unused. The third argument is the string containing script that evaluated via the eval() function when the button is clicked. In this example, the code describes an alert():

 alert('button ' + this.IDNum + ' clicked');
Figure 1: The hierarchy of objects in the framework.

Interestingly, the value of this.IDNum is not defined in the addButton() function or anywhere else in the global namespace. Instead, it’s a member of the buttonWidget object itself. This works because code executed via eval() in the scope of an object inherits the scope of that object, and not the scope of the calling function. The code looks up a specific JavaScript object when we click on the button, and accesses the members of that object from an event fired from a DOM node. But what is IDNum? And how does the environment know which buttonWidget was clicked so it can obtain the right IDNum value?

To answer these questions, we’ll need to look deeper into the framework and the button widget’s responsibilities. By understanding how this simple button widget works, you can leverage the framework to build other widgets from a shared code base.

Here’s how the framework adds the button widget to the page. First, it doesn’t simply add the object returned from the constructor; instead, it adds one of its members, compNode. In other words, the return value from the constructor isn’t a DOM node as you might expect, it’s a JavaScript object, and compNode is a DOM node member of that object. Digging a little deeper, you’ll find that there’s an entire object hierarchy at work here, and that the buttonWidget class is actually a subclass of a base class that’s part of the framework (see Figure 1).

First I’ll show you the base class and you can see what it does, and then I’ll show you the buttonWidget class and you can see how it uses the services provided by the base class to make the end developers’ lives easier.Exploring the components.js File
The first include in the sample page is a file named components.js. This script contains the core of the framework you can build upon to create components and widgets. The ComponentClass() function defines the superclass that buttonWidget eventually inherits from.

Author Note: If you’re unfamiliar with the object-based nature of JavaScript or of prototype-based inheritance, declaring a class as a function may seem odd, but the use of the keyword this should tip you off that the code is a class definition and not a regular function.

   // class constructor for the component superclass   function ComponentClass(){      //   data members      // the DOM node for the component      this.compNode = null;   // default, override in subclass      this.autoGenerate = false;       // -1 signifies that no ID has been set      this.IDNum = -1;       //   class methods      this.setID = function(){         /*implementation not important here*/      }      this.getID = function(){         /*implementation not important here*/      }      // subclasses _must_ redefine generate()      this.generate = function(){return true;}         //   constructor code      // optional DOM node as constructor arg      if(arguments.length > 0){          this.compNode = arguments[0];      }      if(this.autoGenerate){this.generate();   }      // register this component      compReg.register(this); }

The ComponentClass acts as a skeleton for a real widget to fill in, but contains several important methods and properties. The compNode property links the visible DOM node to the JavaScript object that controls its behavior and models the data it presents. I’ll explain this relationship further when you examine the component registry. The setID property gives the component a way to request a globally unique ID number from the framework. The getID property provides an access method which is a shortcut to the property IDNum. The final line of the object definition tells a globally available object, compReg, to register the object being created with the system. The compReg object is a singleton object (only one instance can exist within a page) that “keeps track” of the components you create and use, thereby freeing you from the tedium of manually tying DOM nodes to logical objects and assuring uniqueness.

The ComponentClass class forms the base object type that creates and tracks components in the framework. The class ensures that every framework object has some properties that you can rely upon to provide services to the end developer. The most important of these services is lookup. The compReg object that handles lookup and registration is a single instance of the class ComponentRegistryClass. The framework creates the compReg object and makes it globally available immediately after defining a ComponentRegistryClass in the console.js file.

After that, the framework automatically registers every newly instantiated object that subclasses ComponentClass with the compReg object. The compReg object serves to associate the visible component (DOM node) and its logical (invisible) counterpart?a ComponentClass object. The result frees end developers from the burden of “keeping track” of an arbitrary number of dynamically generated widgets.

Here’s a quick rundown of the compReg object’s methods that enable generic lookup:

compReg.getByNode()?takes single DOM node as only argument. Returns a corresponding logical component object if the passed node is the descendant of a component node in the DOM tree. The method returns null if no ancestor node of the argument corresponds to a component.

compReg.getByID()?takes a component ID string as the only argument. If a component with the given ID is currently registered with the system, the method returns a reference to that component; otherwise, it returns null.

compReg.unregister()?takes a component ID string as the only argument. If a component with the given ID is currently registered with the system, the registered object is deleted. The method does not return a value.

compReg.unregisterByComponent()?takes a single argument?a component object reference. The method deletes the passed component reference from the registry. The method does not return a value.

compReg.register()?takes a single argument?a component object reference?and registers the passed component object as only argument. The method registers the component object with the framework. The method does not return a value.

The getByNode and getByID functions are the most useful, because you can use them to look up the parent object of a component without knowing anything about how and when the component was created. All the functions that accept a component object as argument or return value, assume that the component is either an instance of ComponentClass or an instance of one of its subclasses.Widgets, Widgets Everywhere
Now that you know about the objects providing registration and lookup, you can explore how the example ties everything together to create dynamic user interface components. You should be able to bind components to both data and behavior at creation time (and subsequently modify those bindings) while simultaneously shielding end developers from the complexity of creating the component or specifying its structure and formatting. To accomplishing this goal, the widget object definition must encapsulate its behaviors, and its visible representation, and the component’s code representation (the HTML DOM nodes) must be created dynamically at runtime.

Before you can create a widget, you must first link it to the parent class defined earlier so that it can make use of the lookup and tracking functions. The file button_widget.js defines the sample widget and is the only top-level function/class definition in the file. JavaScript does not provide (good) native inheritance support; therefore you need two steps to subclass from the parent class. The first step is something of a hack, but if repeated several times would allow you to import variables and methods from several parent classes. The first three lines of the class illustrate this.

   this.parent = ComponentClass; // set inheritance   this.parent(); // call parent constructor   // remove parent reference, avoid namespace pollution.   delete this.parent; 

You need a link to the “primary” parent class so that JavaScript can add the class to the scope chain for the buttonWidget class. You create the link with the prototype property. This allows classes that subclass buttonWidget to still be able to “see” its parent classes in the scope chain, which is useful when building composite widgets or when creating highly abstracted interfaces that require hierarchical organization.

   //deep inheritance support   buttonWidget.prototype = new ComponentClass; 

You must examine two more methods of the class buttonWidget before you’ll have a firm grip on how the framework enables truly generic widgets. The generate function is the factory that creates the visible DOM nodes that represent the widget. Separating the code that creates a widget’s visible HTML and the code that models it is important because it lets you change the look and feel of the widget without modifying any calling code. That calling code often contains the most browser-specific code and workarounds, so encapsulating it helps improve portability by limiting the changes needed to provide broader client compatibility. The member function generate provides this factory, creating the HTML element and giving it style. The code to create and style the element used for the button is:

 this.compNode = document.createElement("span");   with(this.compNode.style){      border = "1px solid black";      padding = "2px 5px 2px 5px";      backgroundColor = "#CCCCCC";      color = "black";      letterSpacing = "1px:";      fontFamily = "helvetica, arial, sans-serif";      fontSize = "12px";      lineHeight = "15px";   }

The “with” block simply shortens otherwise repetitive code. Since the code creates the button in the context of the buttonWidget object, you can simply assign the created element to the compNode member. Thus, when you add the compNode member of a button widget in the sample page, you are adding the DOM node created and styled by this code. Later, the function calls setID, which ties the compNode element to the unique ID given to the object, which facilitates speedy lookup via DOM nodes.

The next important section of the generate function assigns an event handler to the DOM node. A lookup function (compReg.getByNode) ties the handler back to the parent object, which then fires a member method of the buttonWidget object.

   this.compNode.onclick = function(evt){      var src = null;      // get a reference to the node that fired the event      // IE specific method      if(document.all){          // use implicit event reference in IE         src = event.srcElement;          // stop event bubbling in IE         event.cancelBubble = true;       }      // standards-compliant method      else{          src = evt.target;         //stop event bubbling on Moz         evt.stopPropagation();         }      compReg.getByNode(src).doAction();   }

To some this may seem like far too much indirection than is strictly necessary, but to provide a scalable widget framework that avoids namespace or object collision, you must implement reference and lookup via a central object. Approaches that attempt to provide uniqueness in widget-specific ways often wind up relying on properties that cannot always be assumed to be unique or duplicating code (providing further opportunity for error). Using a single lookup and reference mechanism for all widgets lets you avoid both hard-to-debug collisions and bloat.

The doAction method of the buttonWidget is also interesting, because it shows how you can build event handling mechanisms that do not depend on assigning specific events to DOM (HTML) elements, but rather on catching an event with a general handler and then calling other functions from that handler. You can use this approach (with a bit more sophistication) to provide dynamic eventhandlers that know nothing about the widgets’ internal structure. Here’s the code for a simplified doAction method that fires actions to be performed when a user clicks the buttonWidget:

   // perform the action registered on the widget   this.doAction = function(){      if(this.fp!=null){this.fp();}      if(this.evalStr!=null){eval(this.evalStr);}   }

The buttonWidget members named fp and evalStr are the second and third arguments passed to the constructor in the example page. If either exist, then they are fired. These properties could just as easily be arrays of strings or function pointers to be called when an event is fired on the widget. The current generic event handling system for the netWindows framework API uses just such an approach. The cut-down framework sample in this article derives from that approach as well.

The approach taken by this system differs from that of other DHTML APIs because it creates widgets and components when they are requested rather than at page-load time. Neither does the sample page create a “pool” of objects which are then selectively hidden and made visible as some DHTML coders tend to do. Instead, the framework defines a component “template” from which new widgets and components of a given type are then “stamped” when needed, and not before. If DHTML interfaces are ever to handle interface state management, this type of behavior is essential. Moving interface state management to the client frees server-side resources to manage session state and perform data manipulation.

The simple API presented in this article for creating generic, reusable widgets and components is just a first step in creating a toolkit that provides DHTML developers with robust tools for creating long-lived event and data-driven interfaces rather than page-refresh driven interfaces. Using DOM-based DHTML and a bit of common plumbing you can create DHTML interfaces with the same level of abstraction currently enjoyed by mature interface toolkits aimed at compiled languages such as C++ and Java.

If you are interested in either developing interfaces using this approach or contributing to the development of the type of robust client-side interface toolkit needed to make the next generation of DHTML applications a reality, check out the netWindows project.. The netWindows API provides a rich set of pre-built widgets, and supports themes and out-of-band content loading, which allows applications to add or change data without losing interface state.

devx-admin

devx-admin

Share the Post:
Apple Tech

Apple’s Search Engine Disruptor Brewing?

As the fourth quarter of 2023 kicks off, the technology sphere is abuzz with assorted news and advancements. Global stocks exhibit mixed results, whereas cryptocurrency

Revolutionary Job Market

AI is Reshaping the Tech Job Market

The tech industry is facing significant layoffs in 2023, with over 224,503 workers in the U.S losing their jobs. However, experts maintain that job security

Foreign Relations

US-China Trade War: Who’s Winning?

The August 2023 visit of Gina Raimondo, the U.S. Secretary of Commerce, to China demonstrated the progress being made in dialogue between the two nations.

Pandemic Recovery

Conquering Pandemic Supply Chain Struggles

The worldwide coronavirus pandemic has underscored supply chain challenges that resulted in billions of dollars in losses for automakers in 2021. Consequently, several firms are

Game Changer

How ChatGPT is Changing the Game

The AI-powered tool ChatGPT has taken the computing world by storm, receiving high praise from experts like Brex design lead, Pietro Schirano. Developed by OpenAI,

Apple Tech

Apple’s Search Engine Disruptor Brewing?

As the fourth quarter of 2023 kicks off, the technology sphere is abuzz with assorted news and advancements. Global stocks exhibit mixed results, whereas cryptocurrency tokens have seen a substantial

GlobalFoundries Titan

GlobalFoundries: Semiconductor Industry Titan

GlobalFoundries, a company that might not be a household name but has managed to make enormous strides in its relatively short 14-year history. As the third-largest semiconductor foundry in the

Revolutionary Job Market

AI is Reshaping the Tech Job Market

The tech industry is facing significant layoffs in 2023, with over 224,503 workers in the U.S losing their jobs. However, experts maintain that job security in the sector remains strong.

Foreign Relations

US-China Trade War: Who’s Winning?

The August 2023 visit of Gina Raimondo, the U.S. Secretary of Commerce, to China demonstrated the progress being made in dialogue between the two nations. However, the United States’ stance

Pandemic Recovery

Conquering Pandemic Supply Chain Struggles

The worldwide coronavirus pandemic has underscored supply chain challenges that resulted in billions of dollars in losses for automakers in 2021. Consequently, several firms are now contemplating constructing domestic manufacturing

Game Changer

How ChatGPT is Changing the Game

The AI-powered tool ChatGPT has taken the computing world by storm, receiving high praise from experts like Brex design lead, Pietro Schirano. Developed by OpenAI, ChatGPT is known for its

Future of Cybersecurity

Cybersecurity Battles: Lapsus$ Era Unfolds

In 2023, the cybersecurity field faces significant challenges due to the continuous transformation of threats and the increasing abilities of hackers. A prime example of this is the group of

Apple's AI Future

Inside Apple’s AI Expansion Plans

Rather than following the widespread pattern of job cuts in the tech sector, Apple’s CEO Tim Cook disclosed plans to increase the company’s UK workforce. The main area of focus

AI Finance

AI Stocks to Watch

As investor interest in artificial intelligence (AI) grows, many companies are highlighting their AI product plans. However, discovering AI stocks that already generate revenue from generative AI, such as OpenAI,

Web App Security

Web Application Supply Chain Security

Today’s web applications depend on a wide array of third-party components and open-source tools to function effectively. This reliance on external resources poses significant security risks, as malicious actors can

Thrilling Battle

Thrilling Battle: Germany Versus Huawei

The German interior ministry has put forward suggestions that would oblige telecommunications operators to decrease their reliance on equipment manufactured by Chinese firms Huawei and ZTE. This development comes after

iPhone 15 Unveiling

The iPhone 15’s Secrets and Surprises

As we dive into the most frequently asked questions and intriguing features, let us reiterate that the iPhone 15 brings substantial advancements in technology and design compared to its predecessors.

Chip Overcoming

iPhone 15 Pro Max: Overcoming Chip Setbacks

Apple recently faced a significant challenge in the development of a key component for its latest iPhone series, the iPhone 15 Pro Max, which was unveiled just a week ago.

Performance Camera

iPhone 15: Performance, Camera, Battery

Apple’s highly anticipated iPhone 15 has finally hit the market, sending ripples of excitement across the tech industry. For those considering upgrading to this new model, three essential features come

Battery Breakthrough

Electric Vehicle Battery Breakthrough

The prices of lithium-ion batteries have seen a considerable reduction, with the cost per kilowatt-hour dipping under $100 for the first occasion in two years, as reported by energy analytics

Economy Act Soars

Virginia’s Clean Economy Act Soars Ahead

Virginia has made significant strides towards achieving its short-term carbon-free objectives as outlined in the Clean Economy Act of 2020. Currently, about 44,000 megawatts (MW) of wind, solar, and energy

Renewable Storage Innovation

Innovative Energy Storage Solutions

The Department of Energy recently revealed a significant investment of $325 million in advanced battery technologies to store excess renewable energy produced by solar and wind sources. This funding will

Renesas Tech Revolution

Revolutionizing India’s Tech Sector with Renesas

Tushar Sharma, a semiconductor engineer at Renesas Electronics, met with Indian Prime Minister Narendra Modi to discuss the company’s support for India’s “Make in India” initiative. This initiative focuses on

Development Project

Thrilling East Windsor Mixed-Use Development

Real estate developer James Cormier, in collaboration with a partnership, has purchased 137 acres of land in Connecticut for $1.15 million with the intention of constructing residential and commercial buildings.

USA Companies

Top Software Development Companies in USA

Navigating the tech landscape to find the right partner is crucial yet challenging. This article offers a comparative glimpse into the top software development companies in the USA. Through a

Software Development

Top Software Development Companies

Looking for the best in software development? Our list of Top Software Development Companies is your gateway to finding the right tech partner. Dive in and explore the leaders in

India Web Development

Top Web Development Companies in India

In the digital race, the right web development partner is your winning edge. Dive into our curated list of top web development companies in India, and kickstart your journey to

USA Web Development

Top Web Development Companies in USA

Looking for the best web development companies in the USA? We’ve got you covered! Check out our top 10 picks to find the right partner for your online project. Your

Clean Energy Adoption

Inside Michigan’s Clean Energy Revolution

Democratic state legislators in Michigan continue to discuss and debate clean energy legislation in the hopes of establishing a comprehensive clean energy strategy for the state. A Senate committee meeting

Chips Act Revolution

European Chips Act: What is it?

In response to the intensifying worldwide technology competition, Europe has unveiled the long-awaited European Chips Act. This daring legislative proposal aims to fortify Europe’s semiconductor supply chain and enhance its