Mass add event handlers to objects in array

I am trying to add onclick event handler to many objects but I can’t understand why it doesn’t work.

I would appreciate nothing too advanced. To assign event handler I use traditional approach as described in http://www.quirksmode.org/js/events_tradmod.html

Heres the code (extract.js):

//the class
function extract(){
	this.iForm=document.getElementById('iNew')
	this.navSel=new Array()
	this.x=0
	this.navSel[this.x]=new Array(3)
	this.navSel[this.x][0]=this.iForm.iVarrantyYears
	this.navSel[this.x][1]=document.getElementById('nSel')
	this.navSel[this.x][2]=document.getElementById('pSel')
	this.x++
	this.navSel[this.x]=new Array(3)
	this.navSel[this.x][0]=this.iForm.bFloors
	this.navSel[this.x][1]=document.getElementById('nSel2')
	this.navSel[this.x][2]=document.getElementById('pSel2')

	this.selectNext=function(select){
		if(select.selectedIndex<select.length){select.selectedIndex+=1}
	}
	this.selectPrevious=function(select){
		if(select.selectedIndex>=1){select.selectedIndex-=1}
	}
}
//function to execute onload
function prepare(){
	o=new extract()
	var c=o.navSel.length
	for(i=0;i<c;i++){
		//The following does NOT work
		o.navSel[i][1].onclick=function(){o.selectNext(o.navSel[i][0])}
		o.navSel[i][2].onclick=function(){o.selectPrevious(o.navSel[i][0])}
	}
	//The following approach works
	/*
	o.navSel[0][1].onclick=function(){o.selectNext(o.navSel[0][0])}
	o.navSel[0][2].onclick=function(){o.selectPrevious(o.navSel[0][0])}
	o.navSel[1][1].onclick=function(){o.selectNext(o.navSel[1][0])}
	o.navSel[1][2].onclick=function(){o.selectPrevious(o.navSel[1][0])}
	*/
	//But obviously I want to add event automatically
}

This JavaScript code is used in extract.html (excerpt):

<html><head>
<script type="text/javascript" src="extract.js"></script>
</head><body onload="prepare()">
<form action="" method="post" id="iNew">
<p>Years of Varranty: <select name="iVarrantyYears" /></select>
<input type="button" id="nSel" value="+" />
<input type="button" id="pSel" value="-" /></p>

<p>Floor count in the building: <select name="bFloors" /></select>
<input type="button" id="nSel2" value="+" />
<input type="button" id="pSel2" value="-" /></p>
</form>
</body></html>

I know that both select tags don’t have options, but I generate them with JS because they hold sequential numbers and this part has no impact on the problem at hand.
Both functions help select next or previous index in a given select tag for greater comfort

Thanks!

The first of the two function needs to be instantiated, which in my example is the (i) part at the end of the function.

Yes, that works! Thank you.

A minor detail remains: how to make this code feel more OOP like. I know JS is not like that but defining two separate functions along the one necessary “prepare()” that is called after onLoad so that all the defined “getElementById” actually work - is there a neat approach? Because those functions have hard-coded variable names anyway - function name “selectNext” and array name “navSel” won’t wokr for different objects.

I noticed the scope problem after some time but my quick fix didn’t work:
o.navSel[i][1].onclick=functionB[/B]{o.selectNext(o.navSel[i][0])}
o.navSel[i][2].onclick=functionB[/B]{o.selectPrevious(o.navSel[i][0])}

Anyway great stuff. It’s always hard to get hung up over syntax errors.

Inside the attached function, the variable i does not have the value that you think it has.
By the time the event occurs, the variable i will always be the same number, that being the length of the array.

Do not create the function from inside the loop. Instead, pass the variables o and i to a function that returns the appropriate function instead.


function selectNextHandler(obj, index) {
    return function () {
        obj.selectNext(obj.navSel[index][0]);
    }
}
function selectPreviousHandler(obj, index) {
    return function () {
        obj.selectPrevious(obj.navSel[index][0]);
    }
}
...
for (...) {
    o.navSel[i][1].onclick = selectNextHandler(o, i);
    o.navSel[i][2].onclick = selectPreviousHandler(o, i);
}

Because the variables are now more removed from their context, I have also renamed o and i to be obj and index within the external functions.

You could always do it this way, so that the inner function retains the scope of the outer one.


o.navSel[i][1].onclick = function(){
    return function () {
        o.selectNext(o.navSel[i][0]);
    }
}(i);
o.navSel[i][2].onclick=function(){
    return function () {
        o.selectPrevious(o.navSel[i][0]);
    }
}(i);

I couldn’t get your code to work. Could you demonstrate using function as return value?

Similar example: selecting radio button fires an event
Javascript (extract.js)

//the class
function extract(){
    this.catRadio=document.getElementById('radioCat')
	
	this.before_filter=function(){
		//here, this assignment didn't work for me
		this.catRadio.onclick=function(){
			return function(){
				o.yuba(o.catRadio,'drop')
			}
		}
	}
	
	this.yuba=function(radio,msg){
		alert('value is '+radio.value+'\
message reads '+msg)
	}
}
//function to execute onload
function prepare(){
    o=new extract()
	o.before_filter()
}

HTML

<html><head>
<script type="text/javascript" src="extract.js"></script>
</head><body onload="prepare()">
<form action="" method="post" id="iNew">
<p><input type="radio" id="radioCat" name="group1" value="category"> <span id="checkCat">Category</span>
 <input type="radio" id="radioSubA" name="group1" value="subA" checked="checked"> <span id="checkSubA">subA</span></p>
</form>
</body></html>