Note: I may be rework the Select Object code to take advantage of the new DynLayer (I wrote this code before I updated the DynLayer).
Recreating select lists with JavaScript and DHTML turned out to be a rather involved little project that I took on. There's a lot of code in the Select object, but the end result is a highly configurable, re-useable, easy-to-use select list. The way this object works is similar to the way a Java applet works. You set a few parameters, add the items in the select list, and it'll generate all the code necessary for it to operate.
The Select object works differently than the Radio and CheckBox objects because it generates CSS on it's own, and it uses absolute co-ordinates during it's operation. This means you can't mark up the select list like you can the other form objects.
Here a screen capture of what the default select list looks like under Netscape/Win95 (text sizes change slightly depending on OS, browser, and fonts installed):
In order to create a list like this you have to supply the image that will be used as the "click-able" part. The rest of the select list is generated with CSS. You can choose all the colors that are used - text color, background color, highlight colors etc, the font-family and size, and even how offset the text is (so that you can center the text). The image that I've used for the "click-able part" (the select bar as I call it) is this:
select.gif (100x16)
The height and width of that image determine the overall dimensions of the select list. These values along with the location of the image must be passed into the object when initializing your select list.
This is the general format for creating a new Select object:
objectName = new Select(name,x,y,image,width,height)
where:
Example:
myselect = new Select("myselect",50,100,"select.gif",100,16)
The value sent for the "name" must be exactly the same as the name of the object because it is internally used as an identifier for itself as well as a naming convention for generated layers.
Important: Because this object generates CSS your Select objects must be defined before the page is done loading - ie. not in the standard init() function).
To add options to the list you use the addOptions() method. The list can support any number of options, you just define the text that will show in the list followed by its associated value in a consecutive list:
objectName.addOptions( text,value, text,value, text,value, text,value)
Examples:
myselect.addOptions( 'Select a Page:','', 'Home','../index.html', 'Clipping','../clipping/clipping.html', 'Nesting Layers','../nesting/nesting.html', 'DynLayer','../dynlayer/dynlayer.html') myselect.addOptions( 'One',1, 'Two,2, 'Three',3)
Though it is not necessary to define your own dimensions, fonts, and colors for your select list, I have built into the Select object many methods and properties by which you can customize your select lists. The following sections will explain how to use them.
Option Sizes
The width and height of the options in the list can be set by the setDimensions() method:
objectName.setDimensions(optionWidth,optionHeight)
Example:
myselect.setDimensions(83,13)
Important: You must call the setDimensions() method before you add the options to the Select list. When you add options to the select list it adds them according to the height of the options. The height of the options must be finilized before any of the options are added.
The setDimensions() method is optional, if you do not define it, it will default to the entire width, and a height of 13 pixels.
Text and List Locations
You can define where the text is located within its confines. This may be necessary when you change the font and size of the text to make the text appear centered. I've included these 5 properties to the object by which you do this:
You can also set the horizontal position of the entire list with respect to the select image by setting the listX property. This is useful when you want to make some kind of weird looking select list.
You can set any one of optionX, optionY, textX, textY, or listX directly:
objectName.optionX = 3 objectName.optionY = -1 objectName.textX = 2 objectName.textY = 0 objectName.listX = 12
Or you can again use the setDimensions() method to define them along with the width and the height:
objectName.setDimensions(optionWidth,optionHeight,optionX,optionY,textX,textY,listX)
Example:
myselect.setDimensions(83,13,3,-1,2,0,12)
If you don't specify any of these dimensions the object will default to (widthOfImage,13,3,0,4,1,0) where widthOfImage is the width of your select bar image.
Font and Size of Text:
Use the setFont() method to change your fonts:
objectName.setFont(fontName,size,other)
where:
Examples:
myselect.setFont('Arial',8)
myselect.setFont('Arial',8,'text-spacing:5pt; line-height:11pt;')
Colors
You set the colors of the list with the setColors() method:
objectName.setColors(bgOff,bgOn,optionOff,optionOn,text,border)
where
If your select list is to be nested within another layer, you must call the setNestInfo() method:
objectName.setNestInfo(nestref,x,y)
where:
If your select list is nested once you'd define it something like this:
myselect.setNestInfo("myparentDiv",100,50)
If it's nested multiple times you pass all the parents separated by ".document.". The x and y values will then be the total of all the left locations of all the parent's left and top locations.
myselect.setNestInfo("myparent1Div.document.myparent2.Div",300,250)
After you've initialized and set all the options for your select object, you also have to set up the HTML and JavaScript in order to draw the select list and allow it to operate.
Immediately following your last setup method you must then call the build() method.
myselect = new Select('myselect',50,50,'select.gif',100,16)
myselect.setDimensions(83,13)
myselect.addOptions(
'Select a Page:','',
'Home','../index.html',
'Clipping','../clipping/clipping.html',
'Nesting Layers','../nesting/nesting.html',
'DynLayer','../dynlayer/dynlayer.html')
myselect.build()
The build() method finalizes your settings and puts together the CSS and HTML for the select list. The build() method creates a property called div which is a String that contains all the DIV tags the select list requires. This string must be document.written somewhere within your BODY.
<BODY onLoad="init()">
<SCRIPT LANGUAGE="JavaScript">
document.writeln(objectName.div)
</SCRIPT>
</BODY>
At some point after the div property is written, you then must activate Select object by calling the activate() method:
objectName.activate()
This can be done immediately after the document.write() or in the default init() function, doesn't matter:
There is one last thing you must do in order to finish off your select list. And that is to pass the co-ordinates from the mouseDown() and mouseMove() functions - the same ones I've used in the Drag and Drop lessons. Easily enough the mouseDown() function calls the Select object's mouseDown(x,y) method, and the mouseMove() function calls the Select object's mouseMove(x,y) method.
function mouseDown(e) {
if ((ns4 && e.which == 1) || ie4) {
if (ns4) {var x=e.pageX; var y=e.pageY}
if (ie4) {var x=event.x; var y=event.y+document.body.scrollTop}
// must send each Select object the mouseDown coordinates
myselect.mouseDown(x,y)
}
}
function mouseMove(e) {
if (ns4) {var x=e.pageX; var y=e.pageY}
if (ie4) {var x=event.x; var y=event.y+document.body.scrollTop}
// must send each Select object the mouseMove coordinates
myselect.mouseMove(x,y)
}
Whew, that's it. I guess I made it sound more difficult than it really is. Here's a complete page showing how to properly set up a select list:
<HTML>
<HEAD>
<TITLE>The Dynamic Duo - Custom Forms [Select Lists] Demo 1</TITLE>
<!-- Source code for Select list is in seleect.js -->
<SCRIPT LANGUAGE="JavaScript" SRC="select.js"></SCRIPT>
<SCRIPT LANGUAGE="JavaScript">
<!--
ns4 = (document.layers)? true:false
ie4 = (document.all)? true:false
myselect = new Select('myselect',50,50,'select.gif',100,16)
myselect.setDimensions(83,13)
myselect.addOptions(
'Select a Page:','',
'Home','../index.html',
'Clipping','../clipping/clipping.html',
'Nesting Layers','../nesting/nesting.html',
'DynLayer','../dynlayer/dynlayer.html')
myselect.build()
function init() {
// must activate each Select object
myselect.activate()
document.onmousedown = mouseDown
document.onmousemove = mouseMove
document.onmouseup = mouseUp
if (ns4) document.captureEvents(Event.MOUSEDOWN | Event.MOUSEMOVE | Event.MOUSEUP)
}
function mouseDown(e) {
if ((ns4 && e.which == 1) || ie4) {
if (ns4) {var x=e.pageX; var y=e.pageY}
if (ie4) {var x=event.x; var y=event.y+document.body.scrollTop}
// must send each Select object the mouseDown coordinates
myselect.mouseDown(x,y)
}
}
function mouseMove(e) {
if (ns4) {var x=e.pageX; var y=e.pageY}
if (ie4) {var x=event.x; var y=event.y+document.body.scrollTop}
// must send each Select object the mouseMove coordinates
myselect.mouseMove(x,y)
}
function mouseUp(e) {
}
//-->
</SCRIPT>
</HEAD>
<BODY onLoad="init()">
<SCRIPT LANGUAGE="JavaScript">
document.write(myselect.div)
</SCRIPT>
</BODY>
</HTML>
View selectlists1.html for this select list example. View Source Code
But we're not quite finished yet. There is a few more things to mention...
By default, the select list will just pop open. But I've added a way to make the select list slide open for an animated effect. Before you call the build() method if you define the slideInc property to an integer, that number will be used as the incremenation in the slide effect. If you set it equal to 10, the list will slide out at 10 pixels at a time. You can also set the speed property to change the repetition speed of the slide (the default is 20):
objectName.slideInc = 10 objectName.speed = 25
View selectlists2.html for an animated select list example. View Source Code
Remember the whole point of having a select list is to be able to use it for some purpose, for a submitting a form to a CGI program, or making a redirection.
To retrieve the value of the selected option, just capture the objects value property.
objectName.value
Before the user has selected any of the options, the value is a string of "undefined". If you'd like the value to default to something else simply overwrite the value before you build() the select list.
View selectlists3.html to check the value of the select list. View Source Code
I've also built my own onChange event handler into the Select object - it works the same way as the onChange handler works with the default form select lists. After the user selects an option, you can define what will happen. You can call another function, or execute any statement you'd like. This allows you to make any sort of "add-on" function you'd like. You could make it automatically submit a form, or redirect the user to another page.
Because of it's usefullness, I've already included a redirection add-on function and I will use it to demonstrate how the onChange handler can be used.
First you make some function that is going to execute when the onChange handler is called:
function SelectRedirect(url) {
if (url!='') document.location.href = url
}
Now assign that function to the onChange handler you specify the function similarly to the way you would with normal HTML:
myselect.onChange = "SelectRedirect(myselect.value)"
View selectlists4.html for a redirection select list example. View Source Code
The select object initializes itself as a Dynamic Layer Object as well. So any of the methods of the Dynamic Layer Object can be used on the entire select list - such as hide(), show(), slideBy() etc. But I made the DynLayer as a property of the select object. To access the DynLayer you use the lyr property:
objectName.lyr.hide() objectName.lyr.show() objectName.lyr.moveTo(80,120) etc.
I've made one more example to show various different setups for the Select object.
View selectlists5.html for variations of the select list. View Source Code
The code for the Select object is shown below excluding the required functions layerWrite(), changeBGColor(), and the Dynamic Layer Object with Slide and Clip Methods. You don't really have to study this code in great depth to know how to use it, but in case you'd like to understand how I built this object feel free to do so.
In all the demos I've included the Select and DynLayer Objects into their own js files, this way you can use the code across many pages.
// Scroll Object
// Copyright 1998 Dan Steinman
// Available at the Dynamic Duo (http://www.dansteinman.com/dynduo/)
// May 23, 1998. Last Updated June 23, 1998.
// In order to use this code you must keep this disclaimer
function Select(name,x,y,image,width,height) {
this.name = name
this.x = x
this.y = y
this.w = width
this.h = height
this.image = image
this.slideInc = null
this.speed = 20
this.optionW = 0
this.optionH = 0
this.totalW = width
this.totalH = this.h+1
this.optionText = new Array()
this.optionValues = new Array()
this.value = "undefined"
this.length = 0
this.hl = -1
this.onChange = null
this.active = false
this.opened = false
this.built = false
this.div = ''
this.css = ''
this.setNestInfo = SelectSetNestInfo
this.setNestInfo(null,0,0)
this.setDimensions = SelectSetDimensions
this.setDimensions(this.w,13,3,0,4,1,0)
this.setFont = SelectSetFont
this.setFont('Arial',8)
this.setColors = SelectSetColors
this.setColors('white','black','black','white','black','black')
this.addOptions = SelectAddOptions
this.open = SelectOpen
this.close = SelectClose
this.mouseDown = SelectMouseDown
this.mouseMove = SelectMouseMove
this.build = SelectBuild
this.activate = SelectActivate
}
function SelectSetNestInfo(nestref,x,y) {
if (ns4 && nestref) {
this.nestref = nestref
this.nest = nestref+'.document.'
}
else {
this.nestref = null
this.nest = ''
}
this.nestX = x
this.nestY = y
this.nestOption = this.nest+this.name+'Div.document.'+this.name+'OptionCtr.document.'+this.name+'OptionDiv'
}
function SelectSetDimensions(optionWidth,optionHeight,optionX,optionY,textX,textY,listX) {
this.optionW = optionWidth
this.totalW = Math.max(optionWidth,this.w)
this.optionHeight = optionHeight
if (arguments.length==7) {
this.optionX = optionX
this.optionY = optionY
this.textX = textX
this.textY = textY
this.listX = listX
}
}
function SelectSetFont(fontName,size,other) {
this.font = fontName
this.size = size
this.other = (other)? other : ""
}
function SelectSetColors(bgOff,bgOn,optionOff,optionOn,text,border) {
this.bgOff = bgOff
this.bgOn = bgOn
this.optionOff = optionOff
this.optionOn = optionOn
this.textColor = text
this.brColor = border
}
function SelectAddOptions() {
for (var i=0;i<arguments.length/2;i++) {
this.optionText[i] = arguments[2*i]
this.optionValues[i] = arguments[2*i+1]
this.length += 1
this.totalH += this.optionHeight
this.optionH += this.optionHeight
}
}
function SelectOpen() {
if (!this.opened) {
this.lyr.clipTo(0,this.totalW,this.totalH,0)
if (this.slideInc==null) {this.listlyr.moveTo(this.listX,this.h); this.opened = true;}
else this.listlyr.slideTo(this.listX,this.h,this.slideInc,this.speed,this.name+'.opened = true')
}
}
function SelectClose() {
if (this.opened) {
if (this.slideInc==null) {
this.listlyr.moveTo(this.listX,-this.optionH-1); this.opened = false;
this.lyr.clipTo(0,this.totalW,this.h,0)
}
else {
this.listlyr.slideTo(this.listX,-(this.optionH+1),this.slideInc,this.speed,this.name+'.opened = false; '+this.name+'.lyr.clipTo(0,'+this.w+','+this.h+',0);')
}
}
}
function SelectMouseDown(x,y) {
if (this.active) {
var xr = this.x+this.nestX+this.listX
var yr = this.y+this.nestY
if (x>=xr && x<xr+this.w && y>=yr && y<yr+this.h) {
if (this.opened) this.close()
else this.open()
}
else if (this.opened) {
if (x>xr && x<xr+this.optionW-1 && y>=yr+this.h && y<yr+this.totalH-1) {
var i = Math.floor((y-yr-this.h)/this.optionHeight)
layerWrite(this.name+'TextDiv',this.nest+this.name+'Div.document.'+this.name+'BarDiv','<SPAN CLASS="'+this.name+'TextStyle">'+this.optionText[i]+'</SPAN>')
this.value = this.optionValues[i]
eval(this.onChange)
}
this.close()
}
}
}
function SelectMouseMove(x,y) {
if (this.opened && this.active) {
var xr = this.x+this.nestX+this.listX
var yr = this.y+this.nestY
if (x>xr && x<xr+this.optionW-1 && y>=yr+this.h && y<yr+this.totalH-1) {
var i = Math.floor((y-yr-this.h)/this.optionHeight)
if (this.hl != i) {
if (this.hl != -1) {
changeBGColor(this.name+'Option'+this.hl+'Ctr',this.nestOption,this.bgOff)
layerWrite(this.name+'Option'+this.hl+'Div',this.nestOption+'.document.'+this.name+'Option'+this.hl+'Ctr','<SPAN CLASS="'+this.name+'OptionOffStyle">'+this.optionText[this.hl]+'</SPAN>')
}
changeBGColor(this.name+'Option'+i+'Ctr',this.nestOption,this.bgOn)
layerWrite(this.name+'Option'+i+'Div',this.nestOption+'.document.'+this.name+'Option'+i+'Ctr','<SPAN CLASS="'+this.name+'OptionOnStyle">'+this.optionText[i]+'</SPAN>')
this.hl = i
}
}
else {
if (this.hl != -1) {
changeBGColor(this.name+'Option'+this.hl+'Ctr',this.nestOption,this.bgOff)
layerWrite(this.name+'Option'+this.hl+'Div',this.nestOption+'.document.'+this.name+'Option'+this.hl+'Ctr','<SPAN CLASS="'+this.name+'OptionOffStyle">'+this.optionText[this.hl]+'</SPAN>')
}
this.hl = -1
}
}
}
function SelectBuild() {
if (this.length==0) {alert("Can't build "+this.name+"\nNo options in list"); return;}
this.css = '<STYLE TYPE="text/css">\n'+
'#'+this.name+'Div {position:absolute; left:'+this.x+'; top:'+this.y+'; width:'+this.totalW+'; height:'+this.h+'; clip:rect(0,'+this.totalW+','+this.h+',0);}\n'+
'#'+this.name+'CoverDiv {position:absolute; left:0; top:0; width:'+this.totalW+'; height:'+this.totalH+'; clip:rect(0,'+this.w+','+this.totalH+',0);}\n'+
'#'+this.name+'BarDiv {position:absolute; left:0; top:0; width:'+this.optionW+';}\n'+
'#'+this.name+'TextDiv {position:absolute; left:'+this.textX+'; top:'+this.textY+'; width:'+this.optionW+';}\n'+
'#'+this.name+'OptionCtr {position:absolute; left:'+this.listX+'; top:-'+(this.optionH+1)+'; width:'+this.optionW+'; height:'+(this.optionH+1)+'; clip:rect(0,'+this.optionW+','+(this.optionH+1)+',0); background-color:'+this.brColor+'; layer-background-color:'+this.brColor+';}\n'+
'#'+this.name+'OptionDiv {position:absolute; left:1; top:0; width:'+(this.optionW-2)+'; height:'+this.optionH+'; clip:rect(0,'+(this.optionW-2)+','+this.optionH+',0);}\n'
for (var i=0;i<this.length;i++) {
this.css += '#'+this.name+'Option'+i+'Ctr {position:absolute; left:0; top:'+(i*this.optionHeight)+'; width:'+(this.optionW-2)+'; height:'+this.optionHeight+'; clip:rect(0,'+(this.optionW-2)+','+this.optionHeight+',0); background-color:'+this.bgOff+'; layer-background-color:'+this.bgOff+';}\n'
this.css += '#'+this.name+'Option'+i+'Div {position:absolute; left:'+this.optionX+'; top:'+this.optionY+';}\n'
}
this.css += '.'+this.name+'TextStyle {font-family:"'+this.font+'"; font-size:'+this.size+'pt; color:'+this.textColor+'; '+this.other+'}\n'+
'.'+this.name+'OptionOffStyle {font-family:"'+this.font+'"; font-size:'+this.size+'pt; color:'+this.optionOff+'; '+this.other+'}\n'+
'.'+this.name+'OptionOnStyle {font-family:"'+this.font+'"; font-size:'+this.size+'pt; color:'+this.optionOn+'; '+this.other+'}\n'+
'</STYLE>'
this.div = '<DIV ID="'+this.name+'Div">\n'+
'<DIV ID="'+this.name+'OptionCtr">\n'+
'<DIV ID="'+this.name+'OptionDiv">\n'
for (var i=0;i<this.length;i++)
this.div += '<DIV ID="'+this.name+'Option'+i+'Ctr"><DIV ID="'+this.name+'Option'+i+'Div"><SPAN CLASS="'+this.name+'OptionOffStyle">'+this.optionText[i]+'</SPAN></DIV></DIV>\n'
this.div += '</DIV>\n'+
'</DIV>\n'+
'<DIV ID="'+this.name+'BarDiv">\n'+
'<IMG NAME="'+this.name+'Img" SRC="'+this.image+'" WIDTH='+this.w+' HEIGHT='+this.h+'>\n'+
'<DIV ID="'+this.name+'TextDiv"><SPAN CLASS="'+this.name+'TextStyle">'+this.optionText[0]+'</SPAN></DIV>\n'+
'</DIV>\n'+
'<DIV ID="'+this.name+'CoverDiv"></DIV>\n'+
'</DIV>\n'
document.write(this.css)
this.built = true
}
function SelectActivate() {
if (this.built) {
this.lyr = new DynLayer(this.name+'Div',this.nestref);
this.listlyr = new DynLayer(this.name+'OptionCtr',this.nest+this.name+'Div');
this.active = true
}
else alert("Cannot activate "+this.name+"\nNot built yet")
}
function SelectRedirect(url) {
if (url!='') document.location.href = url
}
// Required functions are layerWrite(), changeBGColor(),
// and Dynamic Layer Object [in separate js file]
function layerWrite(id,nestref,text) {
if (ns4) {
if (nestref) var lyr = eval('document.'+nestref+'.document.'+id+'.document')
else var lyr = document.layers[id].document
lyr.open()
lyr.write(text)
lyr.close()
}
else if (ie4) document.all[id].innerHTML = text
}
// changeBGColor() function is required
function changeBGColor(id,nestref,color) {
if (ns4) {
if (nestref) var lyr = eval('document.'+nestref+'.document.'+id+'.document')
else var lyr = document.layers[id].document
lyr.bgColor = color
}
else if (ie4) document.all[id].style.backgroundColor = color
}
| Custom Forms: | |
| Buttons | |
| Radio Buttons | |
| Checkboxes | |
| Select Lists | |
| Home | Next Lesson: Scroll Object |