Creating web interfaces with the means of HTML, CSS and JavaScript is relatively easy. One can quickly pick up the basics and begin the work learning the tools deeper in the process. It is possible to create a simple markup and add a bit of dynamics and interactivity in a matter of minutes. The tools are quite open and straightforward for anyone willing to learn.
Still, those technologies have been around for quite a while. HTML origin dates back to 1990. CSS came to see the light of day in 1996 and JavaScript was first introduced in 1995. They have advanced quite far but the basic concepts have remained the same.
On the other hand, web development has progressed a lot over the years. Simple web pages and sites with basic interactivity evolved into big portals with rich user interfaces and very complex high performance web applications. While the development could previously be done with the hands of one man or a small group of fellow programmers, it nowadays involves a certain process, modularization and splitting work within a big team which can even be distributed geographically over many locations.
If you have been involved in web development in a team, you have likely run into one or the other issue while working on the frontend. You've certainly noticed that those basic tools are not quite ready for modular development and team work. They're too simple to carry any modularization strategy with them or provide a framework for split development in a team. If you're using them in a straight fashion without thinking about organization first, it is only a matter of time before your markup and style sheet files grow to such an extent you lose an overview and consequently control over the code.
HTML documents basically represent a hierarchical structure (Document Object Model) of markup elements. When a team decides to undertake development of a particular section of a page, it has to work on a particular subtree (ST1) of a document while being aware of what is being done on the upper level (ST0) and also thinking about the work the other team will perform on another subtree (ST2) of this one (ST1). This is not quite straightforward.
CSS styles can have their effects applied to a single element, propagated to a group of similar elements everywhere in the document or to elements in a particular subtree of a document. Working with styles is dangerous in the sense it can break any random section of the document. In a way, it shares the same issues with procedural programming. Before objects and encapsulation came to play, variables used to be declared in some shared pool accessible to many functions simultaneously. If some function made it wrong, it had immediate repercussions for the rest of the code, leading to hard-to-find-and-debug errors.
JavaScript global variables are also accessible for functions declared everywhere in a document just as the functions themselves have access to any element of the document. One error and it has immediate global effects.
With that kind of raw power at hand, it is very important to think of some organization to minimize these occasional errors and possibly keep their effects contained within a single module. Besides building a complex framework to automatically undertake these tasks for us and abstract low-level details away, there are a few considerations that may help bring some order into chaos, modularize frontend development and help split work within a team.
Add namespaces to global names
One simple solution to secure code from being occasionally damaged is to uniquify the names. Add some prefix to indicate the domain a name belongs to.
For example, imagine you're working on a complex CRM web application. There are pages with user data, orders, invoices, shipment documents and so on. On any of those pages you may want to display a date and the name of the employee responsible for that document or activity.
You could add these elements into your markup in this fashion:
div.document_date { /* … */ }
div.document_employee { /* … */ }
div.activity_employee { /* … */ }
but it is safer to prefix the names with a domain identifier. Say, "ord" for orders, "inv" for invoices and "shp" for shipment information. Now it looks more specific:
div.ord_document_date { /* … */ }
div.inv_document_employee { /* … */ }
div.shp_activity_employee { /* … */ }
The same strategy can be advised for variables and function names:
var inv_document_nr = '20040816753';
function ord_display_user_details () { /* … */ }
Now the chances that some faulty code addressing any common name will have its effects everywhere in the application are very slim. It may seem unnecessarily verbose, but it is better in the long run. And if you worry about the extra traffic these longer declarations will generate, then recall you could add compression to your pages while stylesheets are usually cached by browsers so it's not an issue.
Minimize cascading effects
When using selectors always try to define them to very specifically address the element(s) in question. For example, if you have this structure:
<div class="info">
<div class="date">
<span class="day">14</span>
<span class="month">April</span>
<span class="year">2010</span>
</div>
</div>
and you wish to change the color of the date, you could do it this way:
div.info span { color: #003300; font-weight: bold; }
Looks and works fine until another developer adds a person's name to the "info" box:
<div class="info">
<div class="date">
<span class="day">14</span>
<span class="month">April</span>
<span class="year">2010</span>
</div>
<div class="employee_name">
<span class="first_name">John</span>
<span class="last_name">Smith</span>
</div>
</div>
Now the green color is applied to the employee name as well, which may not exactly be what the developer wanted. If, on the other hand, you wrote your style definition to constrain cascading effects only within the date hierarchy, the effects of it wouldn't have come out to affect anything else:
div.info div.date span { color: #003300; font-weight: bold; }
Or even better, constrain the definition to be very specific:
div.info div.date span.day,
div.info div.date span.month,
div.info div.date span.year
{ color: #003300; font-weight: bold; }
Now the rule will only affect the date display and nothing else.
Avoid anonymous elements
Often you would use unnamed structure elements because there is no immediate reason to assign them names. It is easy to apply styles to them even without them having their own names or identifiers. For instance, you can easily style a menu with any number of unnamed items:
<ul id="main_menu">
<li>Home</li>
<li>Catalog</li>
<li>Contact</li>
</ul>
#main_menu li { color: #990000; text-decoration: underline; }
The code is correct and works. But what if you were to add a few more menus that should be styled differently, for example, your special offers?
<ul id="main_menu">
<li>Home</li>
<li>Catalog</li>
<li>New articles</li>
<li>Top deals</li>
<li>Contact</li>
</ul>
How would you address them differently? They're undistinguishable from the others. You need to add special styles to those new elements:
<li class="item_hot">New articles</li>
<li class="item_hot">Top deals</li>
Doesn't seem like a big deal. But now you wish to capture click events on menu items, separately for normal and special items. You can address the new special elements easily (with jQuery):
$(function()
{ $("li[name='item_hot']").click(function()
{ alert ('Hot item selected!'); }); });
But what about the standard ones? At this point you understand you'd rather assign them names as well for this task and, who knows, you may need them in the future. So you now need to update not only the markup but style definitions as well. Though not complicated but still some work which could have been avoided altogether if you did it in the beginning.
Moreover, nameless elements are most likely to fall victims of some faulty code which targets unnamed elements somewhere in the document. With names assigned to all your elements, there is good chance, a faulty rule definition will fail to match wrong elements and break the rest of the code. It has some hidden value. If you make things wrong but it doesn't affect the application, you have time to fix them silently without the users of your application noticing anything.
Specify absolute paths
When you define selectors, try to specify exact and absolute paths when possible. Consider this example:
<div class="user_info">
<div class="user_name">Sarah Jackson</div>
<div class="user_phone">0123456</div>
<div class="shipment_address">
<div class="address_label">Shipment address</div>
<div class="address">10, Some Avenue, FL 12345</div>
</div>
</div>
You can style the address in this fashion:
div.user_info div.address { color: #003300; }
which is fine and looks like a quite precise selector. But what if more addresses are added later?
<div class="user_info">
<div class="user_name">Sarah Jackson</div>
<div class="user_phone">0123456</div>
<div class="shipment_address">
<div class="address_label">Shipment address</div>
<div class="address">10, Some Avenue, FL 12345</div>
</div>
<div class="alternative_addresses">
<div class="address_label">Other addresses</div>
<div class="address">20, Some Avenue, FL 12345</div>
<div class="address">30, Some Avenue, FL 12345</div>
</div>
</div>
The style definition from above will match all of the address elements, the default address and the additional ones. What if the developer who added these lines expected to see the new addresses printed with some default document style? The above style definition would override it. It would have been a better idea to specify an absolute path to just the element you needed right at that moment:
div.user_info div.shipment_address div.address { color: #003300; }
Even better, specify the precise hierarchy for the elements:
div.user_info > div.shipment_address > div.address { color: #003300; }
The late approach will allow you to immediately notice the differences and inconsistencies after the code is changed. This will force you to have a look at style definitions and update them appropriately. Very likely you'll spot a few more hidden issues you would not otherwise have found so soon had you relied on less restrictive style definitions.
Truncate changes from above
Things can be trickier when a team works separately on different parts of the same page. For example, a page of an online shop can have several areas, like recommendations, hot articles, company news, last customer reviews etc. These page parts can effectively be considered widgets. It is natural for them to be developed separately by different people. Even so, these widgets all belong to one single hierarchy of elements, so the changes in one element are likely to have consequences for the others down the hierarchy. In that case another strategy can be used. For a widget for its top-level element and all of its children redefine the styles so as to reset them to some default standard. For example:
div.recommendations_root, div.recommendations_root *,
div.hot_items_root, div.hot_items_root *,
div.news_root, div.news_root *
{
margin: 0;
padding: 0;
border: none;
/* … */
}
In a sense, it is the same approach as using CSS Reset stylesheets, with the only difference that you apply it several times in your document. When done, it will effectively stop any definitions and changes made up in the hierarchy from crossing the boundary of a root element. The team can develop its widget knowing it is protected from modifications elsewhere.
Of course, you need to make sure that these reset definitions appear after parent definitions but before custom definitions for a given widget. That would require some internal policy on where and how to define styles but it is another matter already.
Avoid local copies
In almost any project, there are shared elements that are used throughout the project. Definitions of the basic styles for input controls and the color scheme of the user interface are the first to mention. It is very much recommended to avoid duplicating these definitions around. A good strategy is to define them separately once and only add more selectors to the chosen styles. For example, instead of copying the same styles into multiple definitions:
div.news_body { color: #333333; font-size: 10pt; }
div.product_description { color: #333333; font-size: 12pt; }
div.warehouse_deals { color: #333333; font-weight: bold; }
you could extract the same text color value and specify it once for several selectors:
div.news_body,
div.product_description,
div.warehouse_deals
{ color: #333333; }
div.news_body { font-size: 10pt; }
div.product_description { font-size: 12pt; }
div.warehouse_deals { font-weight: bold; }
This will help you with updates of the shared styles. When they are kept in a single place, any change will immediately be applied to all "subscribed" elements. If the same definitions are copied and scattered all over the document, there is the risk of you missing a few when you make an update. Keeping them in one place prevents that from happening.
Of course, it may not always be possible to avoid copies. For instance, if you have definitions in several stylesheet files, copies are inevitable. If you attempt to pull definitions from several files and gather them in one place, it though will help in removing the copies but will likely hurt the logical division which has motivated you to split definitions into several files.
Have rules to follow
Whatever your code policy and development strategy are, you absolutely need to have some rules defined for all team members to follow. Even if you are a single individual developer, the rules will help you to keep your mind and code clear. Specify the naming policy, the usage of selectors, the sequence of inclusion of stylesheets into documents and so on. Without the rules, the form-free and structure-less nature of your tools can very quickly turn your work into chaos. So be prepared for it.
The presented recommendations should help you add structure and order to your frontend development and possibly help you with the issues arising with splitting work to do team development. Granted these tips are very simple and rather obvious, you would not immediately think of them. On the other hand, when you once thought about them, it becomes very easy to keep them in mind, adopt them in your work and routinely follow them to produce better code quality.