The Javascript function ‘document.getElementById(“<ElementId>”)’ which returns a reference to the first object within a document with the specified id, is certainly in my experience a highly utilised method. But it’s a bit restricting, certainly in a lot of grid work I’ve done over the past few years.
Why should we restrict the locating of specific elements to just the id attribute? Why not any other attribute? If I have a grid of elements, for example text inputs, I might want to organise them into rows and columns.
<input type="text" id="cost" row="4" col="6"/>
Now if I want to reference this control from the entire page of controls by it’s position without knowing it’s id is ‘cost’, there’s no straightforward way of doing so. It feels like this should already be available to us. For example, perhaps the control directly below this (on row 5 col 6) has focus and we wish to be able to press the up cursor key and have the focus change to this control on row 4 col 6. This would be much like using cursor keys to move around a spreadsheet. If we could search for the controls by some other attribute like row and col this would be easy. Don’t get me wrong, this is completely possible using getElementById, you could simply encode the row and column into the id attribute, as I have done many times before.
<input type="text" id="r4c6"/>...
<input type="text" id="r5c6"/>... control with current focus
Then by parsing the id of the control which currently has focus into row and column variables (r5c6 becomes row=5 and col=6) we can simply subtract 1 from the row, re-encode the id (which would now be r4c6). But the problem is you might want the id to have more meaning than just the position of the control, perhaps calculations are done using several different controls on the page. If you then need to move a control to a different row and/or column you would then need to change all calculations to reference the new grid position. Surely it would be better to reference controls in calculations by a more meaningful id, like ‘travelCost’, ‘hourPerDay’, ‘distance’, etc.
…And this is the very problem that drove me to write the following function, giving the ability to gain a reference to an HTML element via any attribute it might have.
function getElementByAttr(attrName, attrValue)
{
var tags = document.getElementsByTagName("*");
var result = [];
var elem, attr;
var regex = RegExp("^"+attrValue+"$");
var i = 0;
while(elem = tags.item(i++))
{
attr = elem.getAttribute(attrName);
if (attr != null)
if (regex.test(attr))
result.push(elem);
}
return result[0];
}
This is great for picking out elements by a single attribute, but in the case of the row and col scenario it really doesn’t help a lot. So I’m thinking a few more functions are needed for a complete coverage of functionality. What I could do with is a function that can return a subset of the elements, for example an entire row. So I need a getElementsByAttr(attrName, attrValue) (notice the plural Element*s*) function. This would then allow us to do the following which would return an array of references to all elements where the row number is 4 and store them in the variable ‘myRow’:
var myRow = getElementsByAttr("row", "4");
. But we still need to root out the correct column from that row, so another function is needed, or rather an extension of the original as getElementByAttr(attrName, attrValue, arrayOfElements). Then this would allow us to restrict the search to only those elements currently in the arrayOfElements.
//Grab all the elements on row 4.
myRow = getElementsByAttr("row", "4");
//Grab the column 6 element from the array of row 4 elements.
myElement = getElementByAttr("col", "6", myRow);
Additionally the getElementsByAttr function would have it’s uses in instances where you might want to process an entire row or column. E.g. summing the values in a single column.
Assuming a grid with columns and rows numbered from 1 onwards.
total = 0;
myCol = getElementsByAttr("col","9");
for(i=1; i<=myCol.length; i++)
{
newVal = parseFloat(myCol[i].value);
total += (isNaN(newVal))?0:newVal;
}
alert("Total for column 9 = "+total);
When I get chance I’ll post the getElementsByAttr and extended getElementByAttr, but for the last one I need to decide whether to split getElementByAttr(attrName, attrValue) and getElementByAttr(attrName, attrValue, arrayOfElements) into two differently named functions or combine them and allow ‘null’ to be passed for the array when it’s not required.
