JS Applications
JS Overview
Basic JS Usage
DHTML Games
JS Shared Code
Next section...
CGI Scripting
In this Page...
Basic Functions
Global Variables
Get Objects
Simple DHTML
Aliases
Cloaked Email
Cookie Stuff
Support Testing
Site Specific

JavaScript and DHTML Pages

Sharing JavaScript Code

A common ethic in all programming languages is a reluctance to write the same piece of code twice. This goes beyond simple efficiency, careful management of your code will provide you with a useful library of functions which can be quickly and conveniently re-utilised in other projects.

JS code can be shared between webpages

Within this site there are a number of simple JS functions that are used within many of the pages. Rather than add the code separately to each and every page the solution is to write the JS into an external file and then source it within the <head> section of each page.

This site uses a JS file called common.js which contains the basic functions used throughout. The intention here is to show how this file is included and a brief explanation of some of the functions within and why they are useful.
We start with the inclusion line:

<head>
...
<script type="text/javascript" src="/src/common.js">//</script>
...
</head>

If there is to be more than one included JS file then this 'common' file is usually sourced first so that the functions within the other files can access the basic functions defined here. The source files are read in strict order so variables and functions must be defined before they are called.

The Basic Common Functions

Below is the skeleton of the shared JS file, click on each section heading for a description of what it does, why and how.

//  common.js  Shared JS routines
//  (c) Andy Belcher 2006
//
//  Global Variable Definitions
//
var domName    = "dandylife.org";
var wwwName    = "web."+domName;
var DHTML    = 1;
//
//  Object Acquisition
//
function getObjectByAnyMeans(objId) {
  ...
//
//  Simple DHTML Functions
//
function switchClass(lmtId,newClass) {
  ...
//
function switchText(lmtId,newText) {
  ...
//
//  Short-Cut Aliases...
//
function dw(dwStr) {
  ...
//
//  Cloaked Email mailto: Link
//
function antiSpamMailTo(tN,tD,dA,sU) {
  ...
//
//  COOKIE STUFF, read and write 'em
//
function writeCookie(ckName,ckVal,ckDays) {
  ...
//
function readCookie(ckName) {
  ...
//
//  Support tests (Browsers, DHTML, JavaScript and Cookies)
//
function browserUpgradeWarning() {
  ...
//
function dhtmlSupportTest() {
  ...
//
function cookieSupportTest() {
  ...
//
//  Website specific page construction functions
//
  ...
//
//  Temporary warning of construction in progress/missing link
//
function ny() {
  ...
//
//  EOF
//
Global Variable Definitions

As this is usually the first JS file to be loaded this is also a good place to define any values that may be referenced by other functions.

//  Global Variable Definitions
//
var domName    = "dandylife.org";
var wwwName    = "web."+domName;
var DHTML      = 1;
var dhtmlTest  = getObjectByAnyMeans('frame');
//

Here we set a few variables to define the website name for use in various links within the functions, this avoids hard-coding these values which is generally to be avoided.

The DHTML variable signals to the other functions that DHTML is supported. There are various object tests within the system, if any of these fail then DHTML will not work, this flag is set to zero and the functions will detect this and fail/degrade gracefully. The variable dhtmlTest itself is of no consequence, however the function that it calls will have the effect of knocking down the globally declared DHTML flag.

This simple test negates the need for additional tests in the code and will also save execution time by allowing all JS routines requiring DHTML to abort gracefully. An example of this and similar tests is given in the 'Webpage Coding' pages...

Object Acquisition

A key feature of DHTML is that any HTML element within the page can be identified and referenced as an object. The trouble is that some browsers do not do this in a fully compliant way, and to ensure cross-compatibility it is often necessary to test for the existence of the object using a variety of syntaxes.

However, this can add a good deal of unwanted complexity to the code so the easiest way to do this is to define a single function which will return the desired object by whatever means is required, all cross-compatible code is then confined to this one function, all the others can simply reference the returned object directly.

//  Object Acquisition
//
function getObjectByAnyMeans(objId) {
  if(document.getElementById) {return document.getElementById(objId);}
  if(document.all) {return eval("document.all."+objId);}
  if(document.layers) {return eval("document.layers["+objId+"]");}
  DHTML = 0;
  return false;
  }
//

document.getElementById() is the standard function to be used however there are still older non-compliant browsers out there that do not apply the Document Object Model (DOM) in this way and instead reference document.all or document.layers. Each of these definitions of the document object uses a different syntax to reference the specfic page element required.

As soon as the function successfully acquires the desired object it returns with it. Once it has gone through each of the three options without success it assumes that the DOM is not supported and takes down the DHTML flag. (see previous)

Simple DHTML Functions

DHTML can be used to make detailed changes to any of the individual CSS selectors. However the two most common DHTML operations are to change the overall class of an object between states (and thus pick up a pre-defined set of CSS directives) and to change the content of part of the page.

//  Simple DHTML functions
//
function switchClass(lmtId,newClass)
  {if(DHTML) {getObjectByAnyMeans(lmtId).className = newClass;}}
//
function switchText(lmtId,newText)
  {if(DHTML) {getObjectByAnyMeans(lmtId).innerHTML = newText;}}
//

Both functions use the getObjectByAnyMeans() function to identify the required object which takes care of cross-compatibility issues. Each function tests for the DHTML flag and only executes if the flag is set, this prevents incorrect operation if the browser does not support DHTML correctly.

Short-Cut Aliases...

As languages go JS can be rather 'wordy' at times, some of the document.object.method constructs can be very long indeed.

A very simple way to reduce the required volume of code (in this and most other languages) is to create a simple 'wrapper' function...

//  Short-cut aliases...
//
function dw(dwStr) {document.write(dwStr);}
//

This one is very useful and fairly self-explanatory, document.write() is very commonly used and takes up a lot of space in most scripts, using this function it can be abbreviated quite a bit. A similar methodology works for any often repeated piece of code or construct.

Cloaked Email mailto: Link

Spammers are the bane of the Internet, however spamming requires addresses to send to. For anyone who can write Perl (or PHP or similar) it is a trivially simple task to write a script which will get the webpage HTML and then scan it to find, firstly links to other pages so that they too can be scanned, and secondly to find all email addresses embedded within the code.

Email addresses are usually easy to identify within HTML code; firstly they always contain the @ sign, and email links are usually encoded using:
<a href="mailto:fred@domainname.com">Email Fred</a>

Both @ and mailto: are easily found and extracted using pattern-matching routines.

There is nothing more heart-breaking than realising that all of the addresses within your site have just been 'harvested'. Receiving email from spammers on these addresses is bad enough, but a more sinister use for genuine email addresses is as the return address for a spam mail. All of the rejected spam will come back to this address, and your site or organisation appears to be responsible for it! In the event of significant numbers of rejects (bearing in mind that spam is often sent in batches of many thousands at a time) there is a real risk that your mail-servers and mailboxes may be overloaded.

It's almost impossible to make a website, and especially your email addreses 100% spamproof, but not putting email addresses where they can be easily 'harvested' from the page by any automated routine will go a very long way towards achieving this goal. Spammers are by nature lazy people and so nearly all addresses are acquired using these routines rather than manually trawling and entering addresses.

//  Cloaked email mailto: link
//
function antiSpamMailTo(tN,tD,dA,sU) {
  var aS = "&#64;";if(!tN) {tN = "webmaster";}
  var mT = "ma"+"ilt"+"o:";if(!tD) {tD = domName;}
  var tA = tN+"@"+tD;if(!dA) {dA = tN+aS+tD;}
  if(sU) {sU = "?s"+"ub"+"ject="+sU;} else {sU = "";}
  dw("<a href=\""+mT+tA+sU+"\" title=\"Send ema"+"il...\">"+dA+"</a>");
  }
//

The routine itself is pretty straightforwards despite the obfuscation of the code. All of the keywords or symbols such as 'mailto:' and '@' have been hidden from pattern-matching routines by either breaking into several parts or using the HTML entity code &#64; which produces the @ symbol.

An example of this routine is given in the 'Webpage Coding' pages...

Most of the code is there to add suitable defaults for missing arguments.
The first argument is the email name, that is all parts of the address to the left of the @ sign. If the address is within the same domain as the website itself there is no need to specify this as the function will default to the value defined within the common.js file.
The second argument carries the domain name if it is to another site.
The third argument is the text to be displayed within the link, usually the name of the person or group to be emailed. If this is blank it will default to the full email address.
The fourth argument is an optional subject line for the email.

The method relies upon the fact that 'robot' trawling programs do not use JS parsers and are not therefore likely to find anything that is recognisable as an email address. Short of manually picking the code apart the addresses are as well hidden as they can be!

In practice this is very easy to use, these three variations...

1 <script type="text/javascript">
  antiSpamMailTo('fred','somewhereelse.com','Email Fred',
    'DandyWebDesign Demo email...');//</script> at somewhereelse.com

2 Email Fred at this site: <script type="text/javascript" language="javascript">
  antiSpamMailTo('fred');//</script>

3 This is the full default option to raise the webmaster: <script type="text/javascript">
  antiSpamMailTo();//</script>

...give us:
1 at somewhereelse.com
2 Email Fred at this site:
3 This is the full default option to raise the webmaster:

Cookie Stuff, Read and write 'em

Many sites use cookies either to preserve user settings from previous sessions or to identify users. Whatever the usage it is handy to have a simple set of cookie related functions to take care of it all without having to worry about it.

//  COOKIE STUFF, read and write 'em
//
function writeCookie(ckName,ckVal,ckDays) {
  if(!ckName) {return;}
  if(!ckVal) {ckVal = "";}
  if(!ckDays) {ckDays = 1;}
  var expDate = new Date();
  var numDate = expDate.getTime();
  numDate += eval(3600000 * 24 * ckDays);
  expDate.setTime(numDate);
  var ckStr = ckName+"="+ckVal+"; path=/; expires="+expDate.toGMTString()+";";
  document.cookie = ckStr;
  }
//
function readCookie(ckName) {
  if(document.cookie.length > 0)
    {ckStart = document.cookie.indexOf(ckName+"=");
    if(ckStart != -1)
      {ckStart += ckName.length+1;
      ckEnd = document.cookie.indexOf(";", ckStart);
      if(ckEnd == -1) {ckEnd = document.cookie.length;}
      return unescape(document.cookie.substring(ckStart,ckEnd));
      }
    }
  return "";
  }
//

Simple stuff, the first function writes cookies and expects the cookie name, the value and the number of days until it expires. The second function reads the cookie according to its name.

If required support for cookies can also be tested for within the page. This is discussed in more detail in the 'Webpage Coding' pages and in the following section...

Support Testing

The issue of support testing for your pages is discussed in more detail in the webpage coding section under 'Support Testing', follow the links for implementation details for each of these tests.

Here are three very simple JavaScript functions which will add or remove warning banners from the top of the page depending on whether or not the browser type, JavaScript itself, DHTML or cookies are supported in the page.

function browserUpgradeWarning() {
  dw("<div id=\"upgrade\"> "
    + "Sorry, but your browser is either very old or not configured correctly, "
    + "we shall assume the former! "
    + "Some of the styling features within this site will not render "
    + "correctly on your <strong>obsolete</strong> browser. You will still be "
    + "able to use the site but it won't look as good. "
    + "In the meantime you are strongly advised to upgrade your browser to "
    + "<cite>at least</cite> <strong>Internet Explorer 6</strong>, <strong>Netscape6</strong>, "
    + "or one of the other standards compliant browsers such as FireFox, "
    + "Mozilla or Opera, these are fully compliant and will work correctly. "
    + "<br />Try these links for more information: "
    + "<a href=\"http://webstandards.org/act/campaign/buc/\" target=\"_blank\" "
    + "title=\"The Browser Upgrade Campaign...\">&raquo;Browser Upgrade Campaign</a> "
    + "&#8212;<a href=\"http://www.w3.org/QA/\" target=\"_blank\" "
    + "title=\"Find out more about web standards QA from the W3C...\""
    + ">&raquo;W3C QA</a></div>");
  dhtmlSupportTest();
  cookieSupportTest();
  }
//
function dhtmlSupportTest() {
  if(DHTML) {return "";}
  dw("<div class=\"noscript\"><strong>Unable to verify DHTML support!</strong> "
    + "DHTML does not appear to be supported on your browser, without it you "
    + "will not be able to view this site as it was intended. Please "
    + "update your browser and/or settings...</div>");
  }
//
function cookieSupportTest() {
  var cval = 'abc';writeCookie('suptest',cval,1);
  var ncval = readCookie('suptest');
  if(ncval != cval)
    {dw("<div class=\"noscript\"><strong>Unable to write and retrieve "
      + "cookies!</strong> These are either unsupported by your PC/Browser, "
      + "blocked by a firewall or perhaps you have disallowed them in "
      + "your browser configuration. Without cookies you may not be "
      + "able to use this site as it was intended. Please "
      + "update your browser and/or settings...</div>");
    }
  }
//

JavaScript testing is performed using the <noscript> element, this will add a banner if it is not supported.

The browser and its DOM compliancy can be tested using the function browserUpgradeWarning(). This in turn can invoke tests for DHTML support and cookies as separate functions; dhtmlSupportTest() and cookieSupportTest() respectively.

Website Specific Page Construction Functions

One great application for JS is to draw repetitive sections of the page. By writing a specific function to draw any part of the page this function can be easily referenced in every page.

If for example the function is intended to provide a series of links then this data can be coded into an array and then plotted from this.

Note! This can be a very efficient way to structure the links for a website but it also provides links that search engines cannot follow easily. Be very careful here, if you want the site to be unfindable to search engines then carry on, but if you don't then avoid using this method for the primary page links, leave these in plain HTML format so that search engines can 'spider' your pages.

This difficulty with reading JS scripted sections of the page is sometimes useful, for example in the cloaked email link function described above where this renders the address unreadable to spiders.

This site does use a few functions like this, however they are not shown here as they are very specfic to this site. The only function shown here is included as an example, it's one that can be used for sites that are only partially complete and avoids dead-links by showing the user a message and cancelling the navigation. Not exactly ground-breaking and included more as an example than for any practical use - after all a good website does not have incomplete links!

//  Website specific page construction functions
//
//  Temporary warning of construction in progress/missing link
//
function ny() {
  alert('Not yet!\nBut very soon...');
  return false;
  }
//

This <a href="/does/not/exist/yet.html"
  onclick="return ny();">link</a> is not ready yet...

This title="An example of simple JavaScript function to negate an incomplete link..." onclick="return ny();">link is not ready yet...

So there you go, the very basics required to quickly and easily enable simple DHTML within every page of your website!

Show Style-Switcher...