Archive for May, 2007

Rambo 4 Trailer

Monday, May 28th, 2007

Now on youtube, the brand new rambo 4 trailer:

First official John Rambo (Rambo 4) teaser!

Tutorials back online!

Monday, May 28th, 2007

I´ve added the old DHTML Tutorials on the dhtml central blog.

Have Fun!

PS - This is not the final layout!

111111

Making a standards supporting page and menu script

Monday, May 28th, 2007

Intro

In this tutorial I would like to take up some issues about making a site and a menu script that actually follows the standards.

Ok, let’s get an overview over the situation: I want to make a simple site in XHTML , I want it to work in ALL browsers and internet devices. But for the browsers that supports it I want to make a neat menu system.

Because we want to (and can) make the content of the site view able no matter what we have to take into consideration a lot of different things; browsers that doesn’t support stylesheets and/or scripting, browsers that have stylesheets and/or scripting disabled or even PDA’s with small screens et cetera. This means that as a opposite to most of the scripts found on this site the menu have to initially work, then we add scripting and styling.

Making the initial page

.
We start by making the actual page, I will keep it very simple. First I’ll make a logo div:


Then the menu, I’ll make it as an unordered list. That way it will look ok in non-style browsers:

<ul>
	<li>
		<a href="#">About me</a>
		<ul>
			<li><a href="#">Pictures</a></li>
			<li><a href="#">CV</a></li>
		</ul>
	</li>

	<li>
		<a href="#">Portfolio</a>
		<ul>
			<li><a href="#">Sites</a></li>
			<li><a href="#">Design</a></li>
			<li><a href="#">Other projects</a></li>
		</ul>
	</li>
	<li>
		<a href="#">Weblog</a>
		<ul>
			<li><a href="#">Archive</a></li>
		</ul>
	</li>
</ul>

Then some content:

<div id="content">
	<h3>Welcome to my homepage</h3>
	<p>
	Here you can
	learn everything about me.
	</p>

	[dummy text]

</div>

And last but not least the very important and always needed copyright notice:

<div id="content">
	© Copyright 2002 Thomas Brattli

Styling up the page

Again for simplicity of this article I will keep the layout simple. We’ll place the logo on the top, the menu on the right side (just to be different from all the left side menus :} ), the content on the left and the copyright at the bottom.
View the page.

The stylesheet added looks like this:

<style type="text/css">
body{
	font-family: sans-serif;
	padding-left:5%;
	padding-right:5%;
}
#logo{
	font-size:2em;
	border-bottom:1px dotted silver;
	color:silver;
}
#myMenu{
	float:right;
	width:25%;
}
#content{
	float:left;
	width:70%;
	font-size:0.8em
}
#copyright{
	clear:both;
	border-top:1px solid silver;
	margin-top:20px;
	text-align:right;
	padding-right:5px;
	font-size:0.5em;
}
</style>

It’s pretty straigh-forward stuff so I will not go more into that. By now you are probably very impressed with my stylistic powers, but there’s still a lot to do. Next we want to style up the menu.

Styling up the menu and adding script

Because this could be used on large menus I don’t want to set any classnames or anything on the individual menu items so instead I make a function that will loop the entire three and set the classnames for me (note that I added an ID “myMenu” to the top UL surrounding the menu so I can reach it):

function loopElements(el,level){
 for(var i=0;i<el.childNodes.length;i++){
  //We only want LI nodes:
  if(el.childNodes[i] && el.childNodes[i]["tagName"]
   && el.childNodes[i].tagName.toLowerCase() == "li"){
   //Ok we have the LI node - let's give it a className
   el.childNodes[i].className = "myMenu"+level
   //Let's look for the A and if it has child elements
   childs = el.childNodes[i].childNodes
   for(var j=0;j<childs.length;j++){
     temp = childs[j]
     if(temp && temp["tagName"]){
       if(temp.tagName.toLowerCase() == "a"){
       //We found the A tag - set class
       temp.className = "myMenu"+level
      }else if(temp.tagName.toLowerCase() == "ul"){
       //Set class
       temp.className= "myMenu"+level
       //Recursive - calling itself with new element
       //to go all the way through the three.
       loopElements(temp,level +1)
     }
    }
   }
  }
 }
}

This cute little function will loop any UL three you give it, and set classNames to all the LI, UL and A tags. It’s recursive so it can be used on as many levels at you want. After running this function, called like this:

var menu = document.getElementById("myMenu") //Get menu div
menu.className="myMenu"+0 //Set class to the top level
loopElements(menu,0) //Call the function

it will set the className "myMenu"+level to all elements. Which means that to style up all the top level li tags I go like this:

li.myMenu0{
	margin:0;
	padding:0;
	list-style : none;
}

And to style up the actual text I go like this:

a.myMenu0{
	display:block;
	position:relative;
	background-color:silver;
	padding:3px;
	text-decoration:none;
	color:Navy;
	border:1px solid red;
	margin-top:10px;
}

View the page (I really need to start making better looking examples)

But it still doesn’t *do* anything. I’ll add one line to the script which makes all level 1+ ul tags hidden:

}else if(temp.tagName.toLowerCase() == "ul"){
	//ADDED LINE:
	temp.style.display = "none"
	//Set class
	temp.className= "myMenu"+level
	//Recursive - calling it self with the new found element
	//to go all the way through the three.
	loopElements(temp,level +1)
}

View the page
Now we’re getting somewhere, only the main items are open by default, now we just have to add some code that will enable us to open them again onclick:

function showHide(){
	//We have a A tag - need to go to the LI tag to check for UL tags:
	el = this.parentNode
	//Loop for UL tags:
	for(var i=0;i<el.childNodes.length;i++){
		temp = el.childNodes[i]
		if(temp && temp["tagName"]
      && temp.tagName.toLowerCase() == "ul"){
			//Check status:
			if(temp.style.display=="none"){
				temp.style.display = ""
			}else{
				temp.style.display = "none"
			}
		}
	}
	return false
}

We add a onclick event to the a tags:

if(temp.tagName.toLowerCase() == "a"){
	//We found the A tag - let's style it
	temp.className = "myMenu"+level
	//ADDED LINE
	temp.onclick = showHide;
} et cetera...

View the page

Wow, it’s working :) We add some styles, and some small script parts and we end up with this:

View the page

More examples

Just for fun I’ll show you how easy it is to change the entire layout of this page with some simple adjustments to the stylesheet. Remember, still same XHTML markup and same menuscript.

View the page

View the page

Summary

Summary

All examples can be validated at w3c:
Validate xhtml1.1
Validate css
Or viewed with Lynx

All the examples in this article can be viewed on any browser, any screen-size and with or without style/script support. The content will be accessible even though the style and scripting might not be (I know it’s not *perfect*, but it works as an example).

NOTE: In this article I have surfaced a lot of different standards and accessibility concerns. Hopefully this site will soon give you more detailed information about some of the subjects.

Dynamic data using the DOM and Remote Scripting

Sunday, May 27th, 2007

How it works

As most of you probably know the DOM allows us to dynamically create almost any element that can be on a page, so we use it to create a SCRIPT tag that we set a src to. When setting the src of a script tag then adding it to the page the DOM browsers will try to load the script file that we specify.

First I’ll make a reusable function that we can use to load the new content into the page with:

function loadContent(file){
  var head = document.getElementsByTagName('head').item(0)
  var scriptTag = document.getElementById('loadScript');
  if(scriptTag) head.removeChild(scriptTag);
  script = document.createElement('script');
  script.src = file;
	script.type = 'text/javascript';
	script.id = 'loadScript';
	head.appendChild(script)
}

Let’s go line by line;

var head = document.getElementsByTagName('head').item(0)

This just makes a short reference to the head element. Used later in the function.

var scriptTag = document.getElementById('loadScript');

This line tries to get the script tag called loadScript. The first time this function is called this will return null because the tag does not exists.

if(scriptTag) head.removeChild(scriptTag);

If the tag exists we remove it because we don’t want all the old tags to stay in the page. In explorer you don’t really have to do this, you can just set a src to the existing script element but that doesn’t seem to work in Mozilla.

script = document.createElement('script');

This creates a script tag.

script.src = file;

This line sets the src of the script tag to the file that you specify when you call the function.

script.type = 'text/javascript';

We set the type of the script tag to text/javascript

script.id = 'loadScript';

Here we set the ID to loadScript so we can reach it with the second line in the code the next time this function is called.

head.appendChild(script)

Then we add the script tag to the page with the appendChild function. It’s added to the head of the page.

So if you call the function like this:

loadContent('myfile.js')

it’s more or less the same as adding a script tag with regular code like this:

<script type="text/javascript" id="loadScript" src="myfile.js"></script>

Note that the file you load in a script tag like this HAVE to be a JS file. You cannot load another HTML file or whatever like this. It has to valid script inside the file.

Ok, now we know how it works, but what can we do with it?

There are tons of things we can do with this technique, let’s start of with a simple example

Simple example

I had a hard time finding a very simple example for this one so I’ll make a totally useless one just so you can see that it works.

Example 1

This doesn’t really start to get really powerfull before you start using it together with some server-side stuff though. Let’s say you had a page with stock quotes, you could set the script to reload and get the latest stock quotes every 2 minutes and the user would never have to reload the actual page, the only thing updated is the quotes.

Anyway, let’s move on to the menu script

Menu script

Let’s say you have a menu similar to the foldoutMenu from this site with 200 menu items. This is a modified and a little simplified version where the script only loads the top items as HTML (making the older browsers use the main items only), and loads the other elements with the loadContent function only if and when they are needed.

I’ll start by showing you the menu (the first menu and the second sub and subsub menu opens new items)

Example2

This is a simple version of the menu basically to give you the idea (i know, i know, the code is a mess), note that I don’t mean that you should use this script, it’s only intended to help you understand the basics of this technique. Though by all means feel free to reuse the code if you like.

We make the top items manually. That way they will be shown and works in all browsers. The submenus only works in dom browsers. Right now I will not write more about how this works, you’re on your own. The source-code has a lot of comments that should help you understand it (it can be downloaded at the end of the tutorial).

I’ll give you one more example to give you the idea of how I use this script.

ASP Menu script

This part of this tutorial shows an ASP example and you should know some ASP to understand this.

As mentioned before this doesn’t really become useful until you start doing some backend stuff. Let’s say I have a database with 500 products divided in 10 categories and stored in a database (again simplified example).

Database tblCategories:
dbID - autonumber
Catname - Txt
Database tblProducts:
dbID - autonumber
Productname - Txt
Productinfo - Txt
category - Number - related to DBID in table1

You would probably want to add some more info in there, like price and whatever, but you get the idea.

Then we have some data like this in tblCategories:

dbID Catname
1 Software
2 Hardware

And in tblProducts:

dbID Productname Productinfo category
1 Homesite 5 The best code editor around 1
2 44 inch flat screen Use the computer as your home-cinema screen 1

Now we are ready to start displaying the data. The first page displays the categories as topMenus: (note that I code ASP in JScript - so should you!)

//Pointer to the database
database="Provider=Microsoft.Jet.OLEDB.4.0;"
  +"Data Source="+Server.MapPath("example.mdb")+";"

//opening db
q="Select * from tblCategories"
rs=Server.CreateObject("ADODB.Recordset")
rs.open(q,database)

//looping records
while(!rs.EOF){
  name=rs("catname").value
  id=rs("dbid").value
  Response.write('<div level="0" id="m'+id+'" '
  +'scriptFile="products.asp?id='+id+'" class="cl0">'
  +'<a href="old_browsers_link.html" '
onclick="return openMenu(this)"></a>'+name+'</a>'
  +'</div>nn')
  rs.moveNext()
}
rs.close()
rs=null

The second page “products.asp” is that page that will be loaded with the js loading. If you’re thinking “Hey, I thought you said that it had to be js file!!” your right, so we have to get the server to return the content as a javascript file. We do that with this little line:

Response.ContentType="text/js"

So the entire products.asp looks like this:

@LANGUAGE=JAVASCRIPT

//Getting the ASP page to return a js file
Response.ContentType="text/js"

//Getting the id
catid = Request.queryString("id")

//Pointer to the database
database="Provider=Microsoft.Jet.OLEDB.4.0;"
+"Data Source=" + Server.MapPath("example.mdb") + ";"

//opening db
q="Select * from tblProducts where category = "+catid
rs=Server.CreateObject("ADODB.Recordset")

rs.open(q,database)

//looping database records
while(!rs.EOF){
  txt=rs("ProductName").value + " " + rs("ProductInfo").value
  id=rs("dbid").value
  Response.write('addElements("'+txt+'","","show_product_details.asp?id='+id+'")n')
  rs.moveNext()
}
rs.close()
rs=null

Response.write("loaded()")

Again, this was a simple example, and the only thing it does is get the info from the db and display in the menu from the last example. I have not included any product display page or any error checking. But I am sure that if you get the point of this example you’ll see that a similar approuch could be used to load entire tables of data in all kind of cool ways.

 

Download

You can download all the examples from this tutorial here (with the database and ASP files).

More examples on gow to use this technique will come!

Good luck!

Upgrading to CoolMenus 4

Sunday, May 27th, 2007

Intro

I am really sorry to say that I had to change most property/variable names when making the new script. I needed a more stable naming convention for the properties so they are a little different. Shouldn’t really be to hard to update it though, but you DO have to go step by step through the script and change some stuff.

I this tutorial I will actually update the old example 1 from the coolMenus 3 examples step by step while writing.

Remember that this version does not yet support frames so if you’re using coolMenus3 with frames you CAN NOT update yet! The first thing you have to do is update your browsercheck. Remove the entire thing from your page and just use the one in the new JS file (If you site is in a frame and you get problems with NS4 saying “bw is not defined” then move it from the JS file and into your HTML file again.

Menu properties

We’ll start with the menu properties. The first line of the menu creation script looked like this before:

var mDebugging=2

Just delete that line. The old menu creation line was like this:

oCMenu=new makeCoolMenu("oCMenu")

Change that to:

oCMenu=new makeCM("oCMenu")

These:

oCMenu.useframes=0
oCMenu.frame=""
oCMenu.useclick=0

can be deleted since this version does not yet support frames or click activation. You can also delete these lines (if you have them):

oCMenu.useNS4links=1
oCMenu.NS4padding=2

Netscape 4 will now use the same classes as Explorer.

This one:

oCMenu.checkselect=0

Is no longer part of the core script. If you where using this feature you have to get the add-in.js (see the coolMenus documention for more info). So this line can be deleted as well.

oCMenu.offlineUrl="file:///C|/Inetpub/wwwroot/dhtmlcentral/"
oCMenu.onlineUrl="http://www.dhtmlcentral.com/coolmenus/examples/"

The onlineUrl and offlineUrl are replaced with onlineRoot and offlineRoot.

This is basically done because of all the problems the offlineUrl and onlineUrl caused ppl in version 3. Please note that this means you might have to change all your urls :(

These:

oCMenu.pagecheck=1
oCMenu.checkscroll=1

Can also be deleted as they are also part of the add-in.js now.

oCMenu.resizecheck=1

Is the same exept that it’s now called:

oCMenu.resizeCheck=1 //Note the UPPER-CASE "C"

The wait property:

oCMenu.wait=1000

Is the same.

The fromleft, fromtop, pxbetween and menuplacement variables are still the same, only they have changed to upper-case on the second part of their names (all properties have changed this way):

oCMenu.fromLeft=20
oCMenu.fromTop=0
oCMenu.pxBetween=30
oCMenu.menuPlacement="center"

The oCMenu.rows is still the same.

There are two new main properties, fillImg and zIndex. I don’t care about the zIndex for my examples as it’s not important. The fillImg have to be set though, you have to get the cm_fill.gif from the zip file and upload it with the script. The value of the property should be the path to the image from the ROOT of your web:

oM.fillImg="images/cm_fill.gif"
oM.zIndex=0

More info about the menu properties can be found here.

 

Background-bar properties

The usebar, barx, bary, barwidth and barheight properties are the same as before except now they are called:

oCMenu.useBar=1
oCMenu.barWidth="100%"
oCMenu.barHeight
oCMenu.barX=0
oCMenu.barY="menu"

Be sure to change the “B”,”W”, “H”, “X” and “Y” to upper-case letters.

The old barcolor variable is replaced with barClass:

oM.barClass="clBar"

So we have to make a style sheet with a class with settings for the background-bar. See “CSS help” in the documentation for more info about how to make the stylesheet. If I had this before:

oCMenu.barcolor="Navy"

My class will look like this:

.clBar{position:absolute; width:10; height:10; background-color:Navy;
 layer-background-color:Navy; visibility:hidden}

Note that we have to add layer-background-color for NS4 and that the layer needs a width and height for it to be displayed in Opera. Remember to place the class in a stylesheet.

.barinheritborder is replaced with 3 new properties called barBorderClass, barBorderX and barBorderY. In my example I am not using border for the background-bar so I’ll just set them like this:

oM.barBorderX=0
oM.barBorderY=0
oM.barBorderClass=0

More info about the background-bar properties can be found here.

Level properties

There are two main changes in the level properties:

1. The levels now inherit from the last level no matter what.

2. The levels creation code is different:
Before:

oCMenu.level[0]=new Array()

Now:

oCMenu.level[0]=new cm_makeLevel()

I also added another way to create levels for advanced users, See here for more info.

width and height are still the same.

The old .bgcoloroff, .bgcoloron, .textcolor, .hovercolor and .style are replaced by regClass and overClass which now points to CSS classes that holds the styles for the items in the current level.

In my example it looked like this:

oCMenu.level[0].bgcoloroff="Navy"
oCMenu.level[0].bgcoloron="#336699"
oCMenu.level[0].textcolor="White"
oCMenu.level[0].hovercolor="Yellow"
oCMenu.level[0].style=""

So I make two styles in my stylesheet (see CSS help for more info on making the stylesheet):

.clLevel0{
  position:absolute;
  background-color:Navy;
  layer-background-color:Navy;
  color:white;
  padding:2px;
  font-family:tahoma,arial,helvetica;
  font-size:12px;
  font-weight:bold
}
.clLevel0over{
  position:absolute;
  background-color:#336699;
  layer-background-color:#336699;
  color:Yellow;
  padding:2px;
  font-family:tahoma,arial,helvetica;
  font-size:12px;
  font-weight:bold
  cursor:pointer; /* Note the cursor for NS6 */
  cursor:hand /* Cursor for IE */
}

And set the variables to:

oCMenu.level[0].regClass="clLevel0"
oCMenu.level[0].overClass="clLevel0over"

Actually, I would make the CSS classes like this to save space:

.clLevel0,.clLevel0over{position:absolute; padding:2px;
font-family:tahoma,arial,helvetica; font-size:12px; font-weight:bold}
.clLevel0{background-color:Navy; layer-background-color:Navy;color:white;}
.clLevel0over{background-color:#336699; color:Yellow;
layer-background-color:#336699; cursor:pointer; cursor:hand; }

Anyway, moving on the border and bordercolor are replaced by borderX borderY and borderClass . You can now control the vertical and horizontal borders of the items separately. And you control the look of the border with a CSS class. So before I had:

oCMenu.level[0].border=1
oCMenu.level[0].bordercolor=":#006699"

I have to make another CSS class:

.clLevel1border{position:absolute; visibility:hidden;
background-color:#006699; layer-background-color:#006699}

and set the variables to:

oCMenu.level[0].borderX=1
oCMenu.level[0].borderY=1
oCMenu.level[0].borderClass="clLevel0border"

The old .offsetX and .offsetY are the same.

These:

oCMenu.level[0].NS4font="tahoma,arial,helvetica"
oCMenu.level[0].NS4fontSize

Can be removed. NS4 now uses the same classes as the other browsers.

Next is the align variable. It works the same as before only I have added a couple of possible values.

If you where using filters, clip or slide you have to add the add-in.js file to get those properties on the levels again, if now just remove them.

There are four new variables for the levels. They are rows arrow arrowWidth and arrowHeight. Click on them for more information. In my example I don’t need those, and I want all subitems in a column so I’ll add:

oCMenu.level[0].rows=0
oCMenu.level[0].arrow=0
oCMenu.level[0].arrowWidth=0
oCMenu.level[0].arrowHeight=0

in level 0.

Remember to do all these changes for ALL levels that you have any of these values specified.

More info about the level properties can be found here.

Item properties

Most of the item properties are still the same. My old items that looked like this:

oCMenu.makeMenu('top0',',' News','/news/','frmMain2')
 oCMenu.makeMenu('sub00','top0','Newest news','/news/index.asp')
  oCMenu.makeMenu('sub001','sub00','- New DHTML API released',',',160,0)
   oCMenu.makeMenu('sub002','sub00','- Explorer 7 is out',',',160,0)
   oCMenu.makeMenu('sub003','sub00','- Opera 6 supports innerHTML',',',160,0)
 oCMenu.makeMenu('sub01','top0','News archive','/news/archive.asp')

Will still work just fine. The only thing to notice is the links.

With the new onlineRoot and offlineRoot all links now have to be relative to the root of your web. Which means that if you have a file in mypages/myotherpages/myfile.html you have to place in the ENTIRE path like that. This also goes for the image variables; regImage and overImage (before called img1 and img2), if you where using images for the menus.

Other changed variables:
bgcolor, bgcoloron, textcolor and hovercolor are replaces with regClass and overClass (see the level part for more information)

New variables:
rows - You can override the level rows here. align - You can override the level align here. nolink - Used to make a item that doesn’t link.

The onclick, onmouseover and onmouseout are the same.

It now looks like this:

myCoolMenu.makeMenu(name, parent_name, text, link, target, width,
height, regImage, overImage, regClass, overClass , align, rows,
nolink, onclick, onmouseover, onmouseout)

In my example I also had to edit 2 menus that had a onclick event defined, because the adding of the arguments to the function have made the events become one “more to the left”. So I had to add an extra “empty” space in there.

More info about the item properties can be found here.

Finishing

Now remove the line:

oCMenu.makeStyle()

We no longer need this one because the script doesn’t make the styles anymore. Leave the oCMenu.construct() code though. Then after going through the code I got a file like this: menu1.html.

UPDATED: Remember to add the NEEDED styles to the stylesheet:

/* CoolMenus 4 - default styles - do not edit */
.clCMEvent{position:absolute; width:99%; height:99%;
clip:rect(0,100%,100%,0); left:0; top:0;}
.clCMAbs{position:absolute; visibility:hidden; left:0; top:0}
/* CoolMenus 4 - default styles - end */

That’s about it. If this didn’t help let me know in the new CoolMenus 4 forum. And if this helped or you fixed it yourself please come back to the forums and help others do the same. Good luck!

Scripting for 5th Generation Browsers - Part 2

Sunday, May 27th, 2007

Creating Elements

Another powerful tool in the web developer’s arsenal is the document.createElement() method. This method allows developers to create tags on the fly. Essentially, you create a whole document complete with images, text, and whatever else you can think of . However, what is essential to understand when using this method is that what occurs is the creation of a tag, but the tag itself does not have any attributes. For example.

ifrm = document.createElement("IFRAME");

creates an IFrame tag, but it does not define its src attribute or its width and height, nor does it tell it where to place the IFrame in the document body. In order to achieve all the above we can use some of the things that we have learnt previously and create an IFrame from thin air complete with its attributes.

In the following script example we use:
· createElement()
· setAttribute()
· style attributes
· appendChild()

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Creating an IFRAME</title>
<script language="JavaScript" type="text/javascript">
function makeFrame() {
	ifrm = document.createElement("IFRAME");
	ifrm.setAttribute("src", "http://dhtmlnirvana.com/");
	ifrm.style.width = 640+"px";
	ifrm.style.height = 480+"px";
	document.body.appendChild(ifrm);
}
</script>
</head>
<body bgcolor="#000000">
<p><a href="#" onMouseDown="makeFrame()"> Lets make an iframe </a></p>
</body>
</html>

View example

appendChild()
The DOM node method appendChild() is something we have not covered as yet, so lets try and understand its purpose. The purpose of this node method is to give the IFrame a place to reside in the document.

document.body.appendChild(ifrm);

If we follow the logic of the above line of script from a layman’s perspective, this line translates into find the body of the document and place the IFrame in the body. The body is actually a node within the html document so it is important to retrieve it, because this is where all our elements will be displayed.

CSS Dynamic Manipulation

One of the more functional abilities of the new generation browsers is the ability to dynamically manipulate CSS properties by utilizing the DOM. It is just cause to celebrate, because it eases the burden for web developers in a number of ways. Instead of having to use workarounds like document.write to modify CSS properties, finally we can use a standard way of coding that works in 5th generation browsers and above.

Lets begin by exploring a few text effects. First cab of the rank is the ability to dynamically alter letter spacing.

<script language="JavaScript" type="text/javascript">
function spaceLetter(id, amount) {
	document.getElementById(id).style.letterSpacing = amount;
}
</script>

We begin by creating a function spaceLetter() and use two arguments id and amount, which we can then use to define attributes later at the event handler point. The id argument is used in what should now be the familiar document.getElementById(). Then we script the wordSpacing style and give it the same value as our second argument amount.

document.getElementById(id).style.letterSpacing = amount;

This function is then called from an event handler which allows for the manipulation of any elements letter spacing by identifying what the id is ( in this case contentLayer) and the amount that the letters should be spaced to (in this instance 8 ).

<a href="javascript:" onMouseOver="spaceLetter('contentLayer','8')"> Space the Letters </a>

Here is a complete working example to work from:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Letter Spacing Example</title>
<script language="JavaScript" type="text/javascript">
function spaceLetter(id, amount) {
	document.getElementById(id).style.letterSpacing = amount;
}
</script>
</head>
<body bgcolor="#FFFFFF" text="#000000">
<a href="javascript:" onMouseOver="spaceLetter('contentLayer','8')"
onMouseOut="spaceLetter('contentLayer','1')"> Space the Letters </a>
<div id="contentLayer" style="position:absolute; width:470px; height:39px; z-index:1;
  left: 33px; top: 57px; visibility: visible">DHTML
  NIRVANA </div>
</body>
</html>

View example

The same type of scripting method can be applied to many CSS properties. For example, let us suppose we want to dynamically alter the font face, we could use the following script:

<script language="JavaScript" type="text/javascript">
function fontName(id,fontName) {
document.getElementById(id).style.fontFamily = fontName;
}
</script>

and use the following at the event handler point.

<a href="javascript:" onMouseOver="fontName('contentLayer','Verdana')"
onMouseOut="fontName('contentLayer','Courier')"> Change The Font </a>

View example

or if we wanted to dynamically alter a fonts color we could use

<script language="JavaScript" type="text/javascript">
function fontColor(id,color) {
document.getElementById(id).style.color = color;
}
</script>

View example

I think you get the idea. We can of course do a whole lot more than just play around with fonts and that’s what makes coding for the newer generation browsers both intriguing and fun. There are many instances where we do not have to use JavaScript but can just use CSS declarations. Let us imagine we wanted to add OS scroll bars to a <p> element in Internet Explorer 5+ and Netscape 6.

All we need to do is redefine the tag via a CSS declaration as shown below and the <p> tag takes on scrollbars:

<style type="text/css">
p {display: block; overflow: scroll; font-family: Verdana, Arial, Helvetica, sans-serif;
font-size: 12px; position: absolute; height: 100px; width: 200px; left: 400px; top: 10px}
</style>

View example

Object Constructors

Thus far we have focused upon standalone examples to highlight a few of the things that can be achieved by using standards based coding. While useful to illustrate some of the potential of the new generation browsers, in practical web page building terms there are even more useful techniques that can be applied to web page building.

As a way of introduction to more comprehensive articles focused on DOM based web page building techniques, let us take a look at a couple of the primary techniques I often use when building Dynamic HTML web pages.

These techniques include the following:
· Object Constructors
· Object Orientated Browser Sniffer
· Positioning Layers By Browser Dimensions.

Let’s Get Going: In another article at webreference I demonstrated how to dynamically resize images. In keeping with the notion of creating liquid web pages that dynamically accommodate different browser dimensions, let us utilize this opportunity to build on that previous article and demonstrate how to position layers by browser dimensions from a DOM based coding perspective. Specifically, let us learn how to dynamically center a layer across varying browser sizes.

First off our browser sniffer script:

function Is() {
    agent  = navigator.userAgent.toLowerCase();
    this.major = parseInt(navigator.appVersion);
    this.minor = parseFloat(navigator.appVersion);
    this.ns    = ((agent.indexOf('mozilla') != -1) &&
      ((agent.indexOf('spoofer')  ==   -1) && (agent.indexOf('compatible') == -1)));
    this.ns4   = (this.ns && (this.major == 4));
    this.ns6   = (this.ns && (this.major >= 5));
    this.ie    = (agent.indexOf("msie") != -1);
    this.ie3   = (this.ie && (this.major  < 4));
    this.ie4   = (this.ie && (this.major == 4) && (agent.indexOf("msie 5.0") == -1));
    this.ie5   = (this.ie && (this.major == 4) && (agent.indexOf("msie 5.0") != -1));
    this.ie55  = (this.ie && (this.major == 4) && (agent.indexOf("msie 5.5") != -1));
    this.ie6  = (this.ie && (agent.indexOf("msie 6.0")!=-1) );
}

var is = new Is();

The purpose of this script is to be able to detect which browser the client is using. In reality, we could use direct object detection here, instead of browser detection, but in many instances, object detection is not strict enough, particularly if we want to build a complex backward compatible web page or at a minimum redirect users to an alternate page or pass an alert.

The browser sniffer is composed of JavaScript objects that converts the user agent string into an object to use later in the script as a variable by using the var is = new Is(); statement . As long as the variable is associated with the correct user agent string, we should not run into many problems.

From the browser sniffer script we need to use the 6 variables that cover the major DHTML capable browsers: is.ns4, is.ns6, is.ie4, is.ie5, is.ie55 and is.ie6. The easiest way to understand these variables is to think of them as taking the form of a question posed to the browser.

if (is.ie5|| is.ie55|| is.ie6|| is.ns6){
do the script here

Asks the question is this either Internet Explorer 5 and above or alternatively Netscape 6. If the answer to the question is yes, then run this portion of the script. Since this condition forms the basis of coding for the W3C DOM where ever possible it takes priority in the conditional logic. If the answer is no, then go to the next question and see if the answer is yes.

} else if(is.ie4) {
do the script here

Asks the question is this Internet Explorer 4. As before, if the answer to the question is yes then run this portion of the script, if the answer is no then go to the next question.

} else if(is.ns4) {
do the script here

We could also build in other variables, which detect for Opera, WebTV and so on and could either redirect users to an alternate page or pass an alert.

Object Constructors: Having worked our way through the basic premise of detection routines and if/else statements, lets begin to build our JavaScript object construction routine. The purpose of this section of the script is to convert CSS layers into JavaScript objects that can be easily dynamically manipulated later on in the scripting routine.

function layerObject(id,position,left,top,visibility) {
  if (is.ie5|| is.ie55||is.ie6|| is.ns6){
  this.obj = document.getElementById(id).style;
  this.obj.position = position;
  this.obj.left = left;
  this.obj.top = top;
  this.obj.visibility = visibility;
  return this.obj;
  }
}

The script is dedicated to creating attributes for JavaScript objects. A good way to think of this is to conceptualize a CSS layer written in the body section of your document, note how the initial positioning is set to 0 and visibility to hidden. Later on we will dynamically position the layer and also toggle its visibility to visible.

<div id="centerLayer" style="position: absolute; width:200px; height:24px; left:
0px; top: 0px; z-index: 6; visibility: hidden;"></div>

You will notice that in the div tag we have an id attribute. Similarly, in our layer object we also assign an id attribute. The reasoning behind this is that what we are attempting to achieve is a JavaScript replication of the CSS layer attributes. Following this course of logic, we then assign more CSS layer attributes to the layerObject script. We assign, position, top, left and visibility. More attributes could be assigned as needed, for instance we could include z-index, height and width and so on. The object constructor can become very detailed and thus ideally open itself to a myriad of dynamic manipulations. Indeed in later articles, we will be utilizing object constructors a fair bit, so it is important to understand the basic premise of what these allow us to do. For the purposes of this article, lets stick with just a couple of basic attributes to get accustomed to the concepts presented here.

It is important to note, that in reality our object constructor has not really created anything. All we have done is prepared our documents for the creation of new layer objects. Therefore, we need to focus our attention on actually creating new layer objects. This is achieved with the layerSetup() script.

function layerSetup() {
centerLyr=new layerObject('centerLayer','absolute',page_width/2-100,page_height/2-12,'visible')
}

The first line defines the functions name. The second line does quite a few things so lets pull it apart to better understand it.

We create a new variable centerLyr and tell the browser that it equals a new layerObject(). The sole purpose of this section of the JavaScript statement is to hook back into our layerObject() script so that we can then assign the specific values needed for that particular layer. You can identify the attribute value that belongs to layerObject() properties by looking at the following table.

Function Name ID Property Position Property Left Property Top Property Visibility Property
LayerObject id position left top visibility
LayerSetup centerLayer absolute page_width/2-100 page_height/2-12 visible

The upshot of this little piece of scripting is that we end up with a new JavaScript object called centerLayer which has the CSS attributes of positioning, left, top, and visibility assigned to it. From a scripting perspective this is enormously useful because if we so wished we can then manipulate any of the CSS properties as we shall see later on in this article.

Capturing Browser Dimensions

Let’s get back to positioning our layers by browser dimensions. You will note that instead of assigning a numeric value to the left and top attributes of the new layer object, page_width and page_height are used. These are variables created and set at the onLoad event handler:

onLoad="
if(is.ns6) {
page_width=innerWidth;
page_height=innerHeight;
layerSetup();
} else if(is.ie5 || is.ie55||is.ie6) {
page_width=document.body.clientWidth;
page_height=document.body.clientHeight;
layerSetup();
}"

Once again we need to create a little cross browser and backward compatible DOM switch. Netscape 4 and Netscape 6 use innerWidth and InnerHeight to detect a users screen resolution. Therefore, we create a browser detection condition.

if(is.ns6) {

if this condition holds true then the variables page_width and page_height are assigned the correct DOM method for Netscape 6 and the layerSetup() script is triggered.

page_width=innerWidth;
page_height=innerHeight;
layerSetup();

If the condition is not true then attention is focused on the Internet explorer part of the script and the DOM for those browsers is utilized.

} else if(is.ie5 || is.ie55 || is.ie6) {
page_width=document.body.clientWidth;
page_height=document.body.clientHeight;
layerSetup();
}"

Understanding Positioning
Having captured a users browsers page dimensions we can then use the newly formed variables to position the layer by those dimensions. Hence, the values of page_width/2-100, page_height/2, in the layerSetup() script. To determine the meaning of these values, lets think about this in a different way. The value page_width/2-100 literally stands for the browser size divided by 2 and minus 100 pixels from the left edge of the browser.

Dividing the browser size by half, centers the layer. However, its positioning is determined by the top left corner of the layer, something that many people often overlook. Therefore, if the content of the layer is sufficiently wide enough, then the layer displays as not being centered. To overcome this we need to take into consideration the layer width. In this instance, the CSS layer centerLayer width is 200 pixels. We halve that, and recognize that for it to appear centered, we need to move it over to the left by 100 pixels. Hence the -100 in the left value. The same logic is applied to top attribute where the browsers height is divided into half and then the layers height is taken into account.

Let’s look at the completed web page:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>How to Center A Layer</title>
<script>
function Is() {
    agent  = navigator.userAgent.toLowerCase();
    this.major = parseInt(navigator.appVersion);
    this.minor = parseFloat(navigator.appVersion);
    this.ns    = ((agent.indexOf('mozilla') != -1) &&
      ((agent.indexOf('spoofer')  ==   -1) && (agent.indexOf('compatible') == -1)));
    this.ns4   = (this.ns && (this.major == 4));
    this.ns6   = (this.ns && (this.major >= 5));
    this.ie    = (agent.indexOf("msie") != -1);
    this.ie3   = (this.ie && (this.major  < 4));
    this.ie4   = (this.ie && (this.major == 4) && (agent.indexOf("msie 5.0") == -1));
    this.ie5   = (this.ie && (this.major == 4) && (agent.indexOf("msie 5.0") != -1));
    this.ie55  = (this.ie && (this.major == 4) && (agent.indexOf("msie 5.5") != -1));
    this.ie6  = (this.ie && (agent.indexOf("msie 6.0")!=-1) );
}

var is = new Is();

function layerObject(id,position,left,top,visibility) {
if (is.ie5|| is.ie55||is.ie6|| is.ns6){
this.obj = document.getElementById(id).style;
this.obj.position = position;
this.obj.left = left;
this.obj.top = top;
this.obj.visibility = visibility;
return this.obj;
}
}
function layerSetup() {
centerLyr=new layerObject('centerLayer','absolute',page_width/2-100,page_height/2-12,'visible');
}
</script>
<style type="text/css">
<!--
.main {
font-family: Georgia, "Times New Roman", Times, serif;
font-size: 16px;
color: #FBEED5;
text-decoration: none
}
-->
</style>
</head>
<body bgcolor="#999999" onLoad="
if(is.ns6) {
page_width=innerWidth;
page_height=innerHeight;
layerSetup();
} else if(is.ie5 || is.ie55 ||is.ie6) {
page_width=document.body.clientWidth;
page_height=document.body.clientHeight;
layerSetup();
}"
onResize=" history.go(0); ">
<div id="centerLayer" style="position: absolute; width:200px;
height:24px; left: 0px; top: 0px; z-index: 6; visibility: hidden;">
<span class="main">This is a Layer Centered By Screen Resolution</span>
</div>
</body>
</html>

View example

In a sense, a one CSS layer example doesn’t really do justice to the power of using object constructors, because one of the real advantages of using them is code reduction, especially when there are lots of CSS layers used in a web page. It also does not adequately demonstrate the flexibility of object constructors as they can be used with a wide range of purposes in mind. Despite the shortcomings of the above example, it does introduce the notion of object constructors with regards to coding by a singular standard.

Armed with the concepts presented in this article and what is yet to come in future articles, it is my sincere hope that this will prepare web developers for the next generation of scripting techniques.

For more information about these concepts read the DHTML Library tutorial on this site. It has a different but similar approach.

Making a DHTML Script

Sunday, May 27th, 2007

Understanding the library

As mentioned before the reason we want to use a library is to take care of the most basic browser differences and concentrate on making the real code. The library has a lot of methods built in that we can reuse at any time. So when we need to make a script, effect or a menu that’s special for one site we just include the library (with the functions you need) and start coding.

In this tutorial we’ll make a simple but cute dragable menu script where we can drag the “head” of the menu and the rest slides after it. It’s important that you understand the basics about how the library works to be able to follow this tutorial, so if you haven’t please read the DHTML Lib tutorial.

Then let’s start coding!

Starting with HTML and styles

We’ll start by thinking a little about how to add our DIV tags. We need two mayor divs, one for the head part and one parent div for all the items. Then we’ll have one div for each item inside the parent div (in this example we’ll make 4 items). At this point we should also think a little about how the menu should behave on events that occur on the divs. Here’s a little overview:

So we’ll start with the HTML code to add inside the body tag:

<div id="divHead"> Menu</div>
<div id="divMenu">
  <div id="divItem0" class="clItem">Test item1</div>
  <div id="divItem1" class="clItem">Test item2</div>
  <div id="divItem2" class="clItem">Test item3</div>
  <div id="divItem3" class="clItem">Test item4</div>
</div>

Then we need to give the divs some styles:

<style>
#divMenu{
  position:absolute;
  font-family:arial,helvetica;
  font-size:12px;
  width:150;
  height:200;
  clip:rect(0,150,200,0);
  layer-background-color:silver;
  padding:4px
}
#divHead{
  position:absolute;
  font-family:arial,helvetica;
  font-size:12px;
  layer-background-color:#333333;
  background-color:#333333;
  clip:rect(0,150,20,0);
  height:20;
  cursor:move;
  z-index:10;
  width:150;
  padding:2px;
  font-size:13px;
  color:white;
  font-weight:bold
}
DIV.clItem{
  position:absolute;
  font-family:arial,helvetica;
  font-size:12px;
  height:30;
  top:0;
  padding:4px;
  width:150;
  clip:rect(0,150,30,0);
  cursor:hand
}</style>

We place that style tag in the head of our page. I wont talk much about these styles settings now, if you know a little CSS it should be pretty simple to follow. The height,widths and clipping are taken from FIG 1 that where made before I started making the menu. As you can see I have added cursor definitions (NOTE: Setting the cursor only works in Explorer 4+ and Netscape 6) and set the background colors. I set clip values to the same as height and width and layer-background-color to make NS4 cover the entire div with the background color. Did you know many Viagra Potenzmittel Shops using DHTML? They don´t buy Cialis or natürliche Potenzmittel on a dnymic shop, but viagra.

Then we are ready to start with the fun part, the scripting.

Making the script

We start with the easy part, adding a reference to out lib.js file in the head part of our page. (if you haven’t downloaded it yet, see the lib page) Like this:

<script language="JavaScript1.2" src="lib.js" type="text/javascript"></script>

Now we have the libary ready and can start with the code for the menu. First I want to make some global variables for the script. That can be easily changed:
numItems - The number of menu items we want to have.
itemOnColor - The background color to change the item to onmouseover
itemOffColor - The background color to change the item to onmouseout
So we set them like this:

numItems=4
itemOnColor="red"
itemOffColor="silver"

We also need to be able to control where each item goes when clicked so I add an array with links:

itemLinks=new Array()
itemLinks[0]="" 'Note that arrays always start at 0
itemLinks[1]="http://www.bratta.com"
itemLinks[2]="http://www.yahoo.com"
itemLinks[3]="http://www.altavista.com"

The next part of the script is be a init function where all the library objects are made, the placement of the layers are fixed and the script will start:

function init(){
  oHead=new lib_obj('divHead')
  oMenu=new lib_obj('divMenu')
  oMenu.bg(menuBgColor)
  oHead.dragdrop()

  oItems=new Array()
  var h=0
  for(i=0;i<numItems;i++){
    oItems[i]=new lib_obj('divItem'+i,'divMenu')
    oItems[i].moveIt(0,h)
    h+=oItems[i].h
    oItems[i].bg(itemOffColor)
    oItems[i].evnt.onmouseover=new Function("mover("+i+")")
    oItems[i].evnt.onmouseout=new Function("mout("+i+")")
    if(bw.ns4){
      oItems[i].ref.captureEvents(Event.MOUSEDOWN)
      oItems[i].ref.onmousedown = new Function("location.href='+itemLinks[i]+''")
    }else oItems[i].evnt.onclick=new Function("location.href='+itemLinks[i]+''")
  }
  oMenu.clipTo(0,oMenu.w,h,0,1)
  moveM2(10)
}

This function should be called to “start” the menu script, so either onload or just after the HTML divs are loaded. Let’s go line by line:

oHead=new lib_obj('divHead') - Here we make a new lib object called oHead which references the div called divHead.

oMenu=new lib_obj('divMenu') - Here we make a new lib object called oMenu which references the div called divHead.

oHead.dragdrop() - Set drag and drop support for the oHead object.

Then we make a new array which will contain all the items for the menu oItems=new Array() The we loop the array once for each item. Remember that we set the numItems variable above to 4, so this line for(i=0;i<numItems;i++) means that we want to loop from 0 to 4. The reason we use a loop here is because it makes smaller code and that it should be easy to add more elements (just add another HTML div and change numItem to 5). Inside each loop we start of by making each oItem[i] object (i is the counter for the loop):
oItems[i]=new lib_obj('divItem'+i,'divMenu'), then we move the object to left 0 and top h. As you see above h=0, on the line below h+=oItems[i].h we add the height of each element to the h variable so the item will be moved to the height of the previous item. That way the items will be below one another. After that we set the default background color oItems[i].bg(itemOffColor).

Now comes a little interesting part of the code; each lib object has a property called evnt that we use to set events for the object. We remember from FIG 1 that we wanted a onmouseover and a onmouseout event for the item divs that should change the background colors, we will make those functions in a little bit. An event handler set like this doesn’t support any arguments to a function, that’s why we attach the event to a new Function, the argument we want to pass is the number of the current item, so we go like this: oItems[i].evnt.onmouseover=new Function("mover("+i+")").

After that we set the onclick event which will make the page go to the the link for that item when clicked. Netscape 4 doesn’t support onclick events on layers, so we have to use onmousedown, but first we have to capture the event for that div (ref points to the document of that div) oItems[i].ref.captureEvents(Event.MOUSEDOWN), then add the event: oItems[i].ref.onmousedown = new Function("location.href=\'+itemLinks[i]+'\'"). For all other browsers we just set the onclick event like this: oItems[i].evnt.onclick=new Function("location.href=\'+itemLinks[i]+'\'")

We then end by clipping the main menu object to the same height and width as all the items: oMenu.clipTo(0,oMenu.w,h,0,1)
The last line in this function starts another function that will always check if the menu is moved. I will explain that function in a little bit.

Now we’ll make the mover and mout functions that will be called onmouseover and onmouseout of each item. They are pretty straight forward and look like this:

function mover(i){
  oItems[i].bg(itemOnColor)
}
function mout(i){
  oItems[i].bg(itemOffColor)
}

The argument is the number of the item that calls the functions. Then we just set the background color to the itemOnColor or the itemOffColor variables that we defined earlier.

The last but most important function moveM2 (called from last line in init above) looks like this:

function moveM2(inc){
  endx=oHead.x;
  endy=oHead.y+oHead.h
  x=oMenu.x;
  y=oMenu.y
  distx = endx - x;
  disty = endy - y
  num = Math.sqrt(Math.pow(distx,2)+Math.pow(disty,2))/inc
  dx = distx/num;
  dy = disty/num
  if((Math.floor(Math.abs(dx))<Math.floor(Math.abs(endx-x))
  ||Math.floor(Math.abs(dy))<Math.floor(Math.abs(endy-y)))){
    oMenu.moveBy(dx,dy)
  }else oMenu.moveIt(endx,endy)
  setTimeout("moveM2("+inc+")",20)
}

This function will loop itself and run all all the time while the user is on the page. Is basically just checks if there is a difference between the oHead object position and the oMenu object position, and if there is it will slide the oMenu object to the correct position. So when you drag the oHead object this function will notice that there is a difference between the positions and “fix” it. It has one argument; inc which helps us control how fast the oMenu object will slide.

Some of this examples you can see on Wordpress Blogs example Crazylifeblog

Again we’ll go line by line: The first four lines just set some varibles so it will be easier to follow below: endx=oHead.x - The x (left) position that the slide will end at.
endx=oHead.y+oHead.h - The y (top) position that the slide will end at.
x=oMenu.x - The current x (left) position of the oMenu object.
y=oMenu.y - The current y (top) position of the oMenu object.
Then we have two lines that figures out the distance we need to slide for x and y:
distx = endx - x, disty = endy - y.
The next line is pure good old fashion math, to get the slide to move correctly (we want the left and the top “sliding” to end at the same time) we find the number that we will divide the distance with to get the x and y to move the correctly number of pixel each time. We take the square root of the the distance powered by 2 and divided by inc: num = Math.sqrt(Math.pow(distx,2)+Math.pow(disty,2))/inc

The next step is to get the actual pixels to move for x and y; dx = distx/num; , dy = disty/num; We are now ready to start and check whether we should move the menu, I’ll take part one of this line first: Math.floor(Math.abs(dx))<Math.floor(Math.abs(endx-x)) Math.floor just rounds the numbers and makes them integers (rounds down to nearest hole number). Math.abs makes the number positiv no matter if it’s a negative number or not, we need this because we want the menu to move both ways, left and right (or up and down). So this line basically says: if the number of pixels to move x position is lower then the position to end at minus the current position - go on. The second part of that line does the same thing for the y position. If that line evaluates to true we will move the menu by dx and dy pixels: oMenu.moveBy(dx,dy). If it doesn’t evaluate to true we just move the menu straight to the endx and endy.
Ok, one line left, this is an easy one; setTimeout("moveM2("+inc+")",20) - it’s a timeout that calls this function every 20 milliseconds.
Why do we do that you might ask, well there are lots of different ways we could have done it for instance called the function only when the oHead is dragged or similar, but believe me; to make it as stable as possible this is the best way to go. Netscape 4 doesn’t support clearTimeout very well and if we did it another way there’s a pretty big chance we would have gotten some problems with multiple timeouts trying to do the same thing. Though timeouts are pretty heavy on the system it’s ok as long as you don’t have a lot of them going at the same time. Anyway, after that little side track I have good news; We are done!

Now we just need to paste it all together into one page and we have ourselves a brand new DHTML script.

 

The code

<html>
<head>
	<title>Drag menu from DHTMLCentral.com</title>
<style>
#divMenu{
  position:absolute;
  font-family:arial,helvetica;
  font-size:12px;
  width:150;
  height:200;
  clip:rect(0,150,200,0);
  layer-background-color:silver;
  padding:4px
}
#divHead{
  position:absolute;
  font-family:arial,helvetica;
  font-size:12px;
  layer-background-color:#333333;
  background-color:#333333;
  clip:rect(0,150,20,0);
  height:20;
  cursor:move;
  z-index:10;
  width:150;
  padding:2px;
  font-size:13px;
  color:white;
  font-weight:bold
}
DIV.clItem{
  position:absolute;
  font-family:arial,helvetica;
  font-size:12px;
  height:30;
  top:0;
  padding:4px;
  width:150;
  clip:rect(0,150,30,0);
  cursor:hand
}
</style>
<script language="JavaScript1.2" src="lib.js" type="text/javascript"></script>
<script>
/****************************************************************************
Drag menu from DHTMLCentral.com
*   Copyright (C) 2001 Thomas Brattli
*   This script was released at DHTMLCentral.com
*   Visit for more great scripts!
*   This may be used and changed freely as long as this msg is intact!
*   We will also appreciate any links you could give us.
*
*   Tutorial availble at dhtmlcentral.com
*   Made by Thomas Brattli 2001
***************************************************************************/
numItems=4 //How many items?
itemOnColor="red" //Item background color onmouseover
itemOffColor="silver" //Item background color onmouseout

itemLinks=new Array() //Link array - one link for each item
itemLinks[0]=""
itemLinks[1]="http://www.bratta.com"
itemLinks[2]="http://www.yahoo.com"
itemLinks[3]="http://www.altavista.com"

function init(){
  oHead=new lib_obj('divHead')
  oMenu=new lib_obj('divMenu')
  oHead.dragdrop()
  oItems=new Array()
  h=0
  for(i=0;i<numItems;i++){
    oItems[i]=new lib_obj('divItem'+i,'divMenu')
    oItems[i].moveIt(0,h)
    oItems[i].bg(itemOffColor)
    oItems[i].evnt.onmouseover=new Function("mover("+i+")")
    oItems[i].evnt.onmouseout=new Function("mout("+i+")")
    if(bw.ns4){
      oItems[i].ref.captureEvents(Event.MOUSEDOWN)
      oItems[i].ref.onmousedown = new Function("location.href='"+itemLinks[i]+"'")
    }else oItems[i].evnt.onclick=new Function("location.href='"+itemLinks[i]+"'")
    h+=oItems[i].h
  }
  oMenu.clipTo(0,oMenu.w,h,0,1)
  moveM2(10)
}
function mover(i){
  oItems[i].bg(itemOnColor)
}
function mout(i){
  oItems[i].bg(itemOffColor)
}
function moveM2(inc){
  endx=oHead.x; endy=oHead.y+oHead.h
  x=oMenu.x;
  y=oMenu.y
  distx = endx - x;
  disty = endy - y
  num = Math.sqrt(Math.pow(distx,2)+Math.pow(disty,2))/inc
  dx = distx/num;
  dy = disty/num
  if((Math.floor(Math.abs(dx))<Math.floor(Math.abs(endx-x))||
  Math.floor(Math.abs(dy))<Math.floor(Math.abs(endy-y)))){
  oMenu.moveBy(dx,dy)
  }else oMenu.moveIt(endx,endy)
  setTimeout("moveM2("+inc+")",20)
}
onload=init;
</script>
</head>

<body>
<div id="divHead"> Menu</div>
<div id="divMenu">
  <div id="divItem0" class="clItem">Test item1</div>
  <div id="divItem1" class="clItem">Test item2</div>
  <div id="divItem2" class="clItem">Test item3</div>
  <div id="divItem3" class="clItem">Test item4</div>
</div>
Regular content goes here
</body>
</html>

Note that you need the lib.js file that can be found in the DHTML library in the same directory of your file for this to work.

Good luck!

Filters and Transitions

Sunday, May 27th, 2007

Introduction

When Microsoft released Internet Explorer 4 they included a selection of filters and transitions that allow multimedia-style effects in plain HTML web pages. They can be applied to any HTML control, such as DIV tags, form fields or images. Internet Explorer 5 for Windows built on the existing functionality with more advanced transitions and a different method of applying filters. To see the examples in this tutorial you have to be using Internet Explorer 4+.

Static Filters

Internet Explorer 4+

The static filters can be applied to any HTML control through the style tag:

<div id="drop" style="filter: DropShadow(Color=red, OffX=2, OffY=2, Positive=1); width: 100; height:20;">hello</div>

or at runtime through javascript:

document.all["drop"].style.filter = "DropShadow(Color=red, OffX=2, OffY=2, Positive=1)";

both produce this

red drop shadow(note: for some reason you must specify the width and height attributes for the filter to work)

Here is a list of the filters you can use:
» alpha alpha blending (aka transparency)
» blur motion blur
» chroma makes a specified colour transparent
» dropShadow creates a silhouette of the object
» flipH flips the contents of the object horizontally
» flipV flips the contents of the object vertically
» glow creates a glow effect for the object
» gray converts contents to grayscale
» invert inverts the colours of the object
» mask produces a mask of a specified colour
» shadow like drop shadow except edges are blurred
» wave adds a ripple to the image
» x-ray seems to be like an OR function

The best way to use these filter properties is to use WebFX’s Filter Tool, you can also check the Microsoft Documentation, except this has been updated to work with the IE5.5+ method of applying filters.

Internet Explorer 5.5+

With IE5 Microsoft have changed the way filters are applied, they still use the filter property of the style tag, but uses different syntax. In HTML:

style="filter:progid:DXImageTransform.Microsoft.DropShadow(Color=red, OffX=2, OffY=2, Positive=1)”;

and from code:

document.all["drop"].style.filter = "progid:DXImageTransform.Microsoft.DropShadow(Color=red, OffX=2, OffY=2, Positive=1)”;

If you look at the Microsoft Documentation you will notice several other filters such as Light, Compositor and Matrix. These work a little differently than the others and probably best if you look at the specific documentation when you are interested.

 

Transitions

These (as far as I am aware) only work in Internet Explorer 5.5+ for Windows. It is therefore necessary to start off with a browser and OS check. See the DHTML library tutorial for more info about that.

First off here is a list of the transitions that you can use:
» barn resembles a door opening or closing
» blinds appears to open or close blinds
» checkerBoard squares are uncovered like a checkerboard over original content
» fade fades new content in
» gradientWipe passes a gradient band over the old content
» inset reveals new content diagonally
» iris produces an effect like the opening of a camera
» radialWipe like a windscreen wiper
» randomBars exposes random lines of pixels one at a time
» randomDissolve exposes random pixels one at a time
» slide new content slides in over old content
» spiral reveals new content in a spiral motion
» stretch stretching motion to cover original content; like a cube rotating
» strips moves successive strips into place
» wheel like spokes of a wheel uncovering new content
» zigzag reveals new content with a forward and backwards motion down the object

Now we need to know how to use them. To find the properties of each transition look at the Microsoft Documentation (if I find the time I may make a utility similar to the WebFX one linked above).

We’ll start off with the fade transition that takes a single property “duration”:

All that is gold does not glitter,
Not all those who wander are lost;
The old that is strong does not wither,
Deep roots are not reached by the frost.
do fade

Now here is the function that does the above:

function doFadeImage() {
//create object reference
var el = document.all["divFader"];

//add the filter to the style tag
el.style.filter = "progid:DXImageTransform.Microsoft.Fade(duration=1)";

//apply the filter
el.filters[0].apply();

//change the object
if(state) {
el.style.backgroundColor = "#BDAA91";
el.style.color = "#000000";
el.innerHTML = verse1;
state = 0;
} else {
el.style.backgroundColor = "#000000";
el.style.color = "#BDAA91";
el.innerHTML = verse2;
state = 1;
}

//play the filter
el.filters[0].play();
}

Now this may seem a bit complicated so I’ll brake it down into 4 important stages:
1. Add the filter to the style attribute, either through code or in the HTML
2. Use the code el.filters[0].apply(); to initiate the transition
3. Make the changes to the object through code
4. Play the changes using el.filters[0].play();

To see how to use transitions with the CoolMenus script check example 14 where you can try all the different filters.

Good luck!

Appendix A - The Matrix Transformation

In the field of Computer Graphics matrices are used to speed up and simplify certain calculations. For example, your 3D-graphcs card has special hardware that uses a 4×4 matrix to rotate, translate or stretch points in 3-dimensions, thus speeding up calculations that are normally done by the main processor.

In Internet Explorer 5, Microsoft have included a Matrix Filter. This, obviously, only works in 2-dimensions but it can be used to apply a transformation to all pixels in an object.

Matrix Math
To get you started here is a brief introduction to 2D matrix multiplication:

x * a b = x*a + y*b
y c d x*c + y*d

Now, if this is applied to each pixel in an image, we can get some very interesting results.

Example: rotation
In order to rotate a point around the origin (0,0) you can find the new values of x and y using the following equations (if you don’t believe here’s a proof for one of the equations):

newX = x*cos(Ø) - y*sin(Ø)
newY = x*sin(Ø) + y*cos(Ø)

Now it can be seen that we can use the following matrix to rotate a point:

x * cos(Ø) -sin(Ø) = x*cos(Ø) - y*sin(Ø)
y sin(Ø) cos(Ø) x*sin(Ø) + y*cos(Ø)

Using the Matrix Filter

Here is list of the matrix filter’s properties:
» FilterType bilinear - uses an average of the 4 nearest pixels; smoother but slower nearest - nearest neighbour, uses best approximation, faster but jagged
» SizingMethod clip to original - if transformed image exceeds the initial dimensions it will be clipped to fit
auto expand - automatically increases/decreases size of container
» M11, M12, M21, M22 the values of the 2D-matrix (a,b,c,d as above)
» Dx, Dy used for linear transformations

The matrix filter can be applied through either the style tag or using code. To apply it using the style tag apply the properties use the same method that is described for normal filters. In code it is slightly different:

var el = document.getElementById(name);
el.style.filter = "progid:DXImageTransform.Microsoft.Matrix()";
el.filters.item("DXImageTransform.Microsoft.Matrix").[property] = [value];

Example
Example only works on explorer 5+ on windows. This example uses the rotation matrix described above to cause the box to spin. It should be noted that the code has to take into account that the dimensions of the container change size in order to keep the box centred.

Here’s the code for the function that will rotate an object by an arbitrary angle:

function rotate(name, angle){ //convert angle into radians
    var rad = degToRad(angle);

    //calculate cos and sin of the angle
    costheta = Math.cos(rad);
    sintheta = Math.sin(rad);

    //create object reference
    var el = document.getElementById(name);
    if(el) {
        //apply the filter
        el.style.filter = "progid:DXImageTransform.Microsoft.Matrix()";

        //set up the properties
        el.filters.item("DXImageTransform.Microsoft.Matrix").SizingMethod = "auto expand";
        el.filters.item("DXImageTransform.Microsoft.Matrix").FilterType = "bilinear";

        //apply the rotation matrix transformation
        el.filters.item("DXImageTransform.Microsoft.Matrix").M11 = costheta;
        el.filters.item("DXImageTransform.Microsoft.Matrix").M12 = -sintheta;
        el.filters.item("DXImageTransform.Microsoft.Matrix").M21 = sintheta;
        el.filters.item("DXImageTransform.Microsoft.Matrix").M22 = costheta;
    }
}

This code calls the rotation function and recentres the DIV. In order to do this we need to use the offset values since the style’s properties aren’t updated:

var angle = 0;
function doRotate() {
    //check to make sure angle is between 0 and 360 degrees
    if(angle>360) angle-=360;
    //increment angle
    angle+=15;

    //do rotation
    rotate("aDiv",angle);

    //recentre div using offset values
    var el = document.getElementById("aDiv");
    el.style.top = 25 - (el.offsetHeight/2);
    el.style.left = 25 - (el.offsetWidth/2);

    setTimeout("doRotate();",10);
}

And there you have it! Have fun playing, and if you want to get further information on the Matrix Filter visit the MSDN site.

Scripting for 5th Generation Browsers Part 1

Sunday, May 27th, 2007

Introduction

(more…)

DHTML library

Sunday, May 27th, 2007

Object Oriented Programming (OOP) and DHTML

We’ll divide these lessons in 3 pages guiding you through the building of objects and association of funtions to them. At the beginning we’ll just use pointer variables to explain what objects do, and then we’ll explain how to properly build objects to assign functions and properties to them, in order to save up some space and make the code reusable. Finally, before we start to discuss the library code, we’ll show you a simple, usable, example to move objects around the window. So here we go.

Basically, just for starting, you can see an object like a pointer to a layer. For instance, if you have layerA, using an object constructor you would avoid having to refer to that layerA as: document.layerA for ns4, document.all.layerA for ie4+ or document.getElementById('layerA') for ns5 and ie5+ depending on the browser, everytime you want to use or do anything to that layer.

Once you get this, everything is easier. :-) As an example, a simple (very improveable) object constructor could be:

if(bw.ns4) {lyrAObj= document.layerA}
if (bw.ie4 || bw.ie5) {document.all.layerA}
if (bw.ns5 || bw.ie55 || bw.ie6) {document.getElementById('layerA')}

But this is too long, so we could use booleans to shorten this a little. A boolean is a way to compare different values in a true/false condition, giving different results depending on the comparison result. The basic use are as follow: varA=(a==3)?1:0 will return varA being true if a is equal to 3 and false if not. To link different comparisons, we can use:

varA=(a==1)?"case one":(a==2)?"case two":(a==3)?"case three":0

and so on. In the first case, if a==3 varA will be true, whereas in case two will be “case three”. This way we can now look at the constructor again:

lyrAObj= (bw.ns4)?document.layerA:(bw.ie4 || bw.ie5)?document.all.layerA:
  (bw.ns5 || bw.ie55 || bw.ie6)?document.getElementById('layerA'):0;

or

lyrAObj= (bw.ns4)?document.parentLayer.document.layers.layerA:
  (bw.ie4 || bw.ie5)?document.all.layerA:(bw.ns5 || bw.ie55 || bw.ie6)?
  document.getElementById('layerA'):0;

for nested layers (very important issue in netscape 4), assuming of course you are using Thomas browsercheck function (more on this later).

As we can see, the comparison test would be “Is the object bw.ns4 true?”. If it’s true, then the new object lyrAObj will have the value document.layerA, and so on.

Well, the example above tried to do a simple introduction to defining and using objects. But as I said before, the object constructor is a simple one. As you guessed it, if you want to make a new object you will have to write these lines for any object you would need. The solution is using a true object constructor that automatically retrieves all these informations and lets you use any object with any associated function easily.

Move method

I’ll start with a very basic example on how we can use this in the real world. Here we’ll make an object move around the page. The following code uses the regular browsercheck script, so we’ll start with this one.

//Browsercheck (needed) ***************
function lib_bwcheck(){
  this.ver=navigator.appVersion
  this.agent=navigator.userAgent
  this.dom=document.getElementById?1:0
  this.opera5=this.agent.indexOf("Opera 5")>-1
  this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
  this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
  this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
  this.ie=this.ie4||this.ie5||this.ie6
  this.mac=this.agent.indexOf("Mac")>-1
  this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
  this.ns4=(document.layers && !this.dom)?1:0;
  this.bw=(this.ie6||this.ie5||this.ie4||this.ns4||this.ns6||this.opera5)
  return this
}
bw=new lib_bwcheck() //Browsercheck object

As you can see the browsercheck is in fact an object constructor function lib_bwcheck, several properties such as ie5 or ns4 and the instance that really builds the object bw is bw=new lib_bwcheck(). This way, when the browser reads this code it builds an object and assigns true to its actual case. For instance, if you’re surfing with ns4, the script will come out with bw.ns4 as true. This is nice to make comparisons later and use the appropiate code for each browser.

Now we are going to write the layer into the body. We make a div called divTest in the HTML code like this:

This is a test div!

<div id="divTest" style="position:absolute; left:10; top:800; background-color:red; ; color:white">
This is a test div!
</div>

Then below that we add this code (inside a script tag):

function lib_moveIt(x,y){
  this.css.left=x;
  this.css.top=y;
}

function lib_obj(obj){
  if(bw.ns4)this.css=document.layers[obj]
  else if(bw.ie4)this.css=document.all[obj].style
  else if(document.getElementById) this.css=document.getElementById(obj).style
  this.moveIt=lib_moveIt
  return this
}
oDiv=new lib_obj('divTest')

And we call the moveIt method of the object to move the object around like this with the arguments x and y which are the left and top values we want to move the object to:

That worked very well now didn’t it? The big advantage here is that this is very reusable code and you make as many objects like this you want in a page.

This code should more or less explain itself. It’s the same as the previous examples except that we make the this.css property, which is different depending on which browser the user is using. You’ll have to see this as the path the browser takes to reach the layers, which of course, due to the differences among browsers and their implementation of DOM, is different for each. The this.css points to the already existing object in the browser, so we can use the already existing properties and methods of that object like we want. Like in the lib_moveIt function where we use the regular .left and .top properties of the object.

Let me try and explain what happens here, first if the browser is Netscape 4 this is how it will behave/look:

function lib_moveIt(x,y){
  this.css.left=x
  this.css.top=y
/*
When this function is called with NS4
(NOTE: This is a function, but it's a method of the lib_obj objects!)
this.css = document.layers["divTest"]
Therefore when I assign a value to this.css.left I am basically
just assigning a value to document.layers["divTest"].left
*/
}

function lib_obj(obj){
  if(bw.ns4)this.css=document.layers[obj] //NS4
  else if(bw.ie4)this.css=document.all[obj].style //IE4 - IE5
  else if(bw.dom) this.css=document.getElementById(obj).style
  this.moveIt=lib_moveIt
return this
}
oDiv=new lib_obj('divTest')
/*
NS4 Will return an object with these properties and methods:
oDiv = [object object]
oDiv.css = document.layers["divTest"]
oDiv.moveIt=lib_moveIt (calls the lib_moveIt function)
*/

If you have Explorer 4 it will look like this:

function lib_moveIt(x,y){
  this.css.left=x
  this.css.top=y
/*
When this function is called with IE4
this.css = document.all["divTest"].style
Therefore when I assign a value to this.css.left I am basically just
assigning a value to document.all["divTest"].style.left
*/
}
function lib_obj(obj){
  if(bw.ns4)this.css=document.layers[obj] //NS4
  else if(bw.ie4)this.css=document.all[obj].style //IE4 - IE5
  else if(bw.dom) this.css=document.getElementById(obj).style //NS6
  this.moveIt=lib_moveIt
  return this
}
oDiv=new lib_obj('divTest')
/*
IE4 Will return an object with these properties and methods:
oDiv = [object]
oDiv.css = document.all["divTest"].style
oDiv.moveIt=lib_moveIt (calls the lib_moveIt function)
*/

If you have Explorer 5+ or Netscape 6 it will look like this:

function lib_moveIt(x,y){
  this.css.left=x
  this.css.top=y
/*
When this function is called with IE5+ or NS6
this.css = document.getElementById(obj).style
Therefore when I assign a value to
this.css.left
I am basically just assigning a value to
document.getElementById(obj).style.left
*/
}
function lib_obj(obj){
  if(bw.ns4)this.css=document.layers[obj] //NS4
  else if(bw.ie4)this.css=document.all[obj].style //IE4 - IE5
  else if(bw.dom) this.css=document.getElementById(obj).style //NS6
  this.moveIt=lib_moveIt
  return this
}
oDiv=new lib_obj('divTest')
/*
IE5+ or NS6 Will return an object with these properties and methods:
oDiv = [object]
oDiv.css = document.getElementById(obj).style
oDiv.moveIt=lib_moveIt (calls the lib_moveIt function)
*/

We know that this might be hard to understand, but really don’t know what more to say. Try and play with this code, add and remove properties and methods to figure out how this works.

We’ll do 2 more things before we look at the “real” code of the library. First off, taking the code from the top of this page and compress it a little to make it a little nicer:

function lib_moveIt(x,y){
  this.css.left=x; this.css.top=y
}
function lib_obj(obj){
  this.css=bw.ns4?document.layers[obj]:bw.ie4?document.all[obj].style:
    bw.dom?document.getElementById(obj).style:0;  //This is one line!
  this.moveIt=lib_moveIt
return this
}
oDiv=new lib_obj('divTest')

We had previously seen the use of booleans, so this should sound logical. It’s basically the same as using if / else statements but shorter and more elegant.

Then we will use the prototype property of any javascript object that makes it possible to add methods and properties to an object constructor (and therefore to ALL objects created with that constructor), without being defined inside the constructor itself. This is helpful because we can create objects and then assign new properties to them. In this case the constructor is the lib_obj function. This makes it simpler to add functions:

function lib_obj(obj){
  this.css=bw.ns4?document.layers[obj]:bw.ie4?document.all[obj].style:
  bw.dom?document.getElementById(obj).style:0; //This is one line!
  return this
}
lib_obj.prototype.moveIt = function(x,y){
  this.css.left=x; this.css.top=y
}
oDiv=new lib_obj('divTest')

As you can see the object constructor doesn’t need the this.moveIt= lib_moveIt anymore. The syntax is the following:

constructorname.prototype.methodname = function(arguments) { }

The code

Go to project library to see the code

When you have looked through this one you can go to my new library section where you can go trough each function and get them explained.