Perlessence
Fun with JS: Element.parentTag
Here at Gaia we have a swapSubmit function that allows you to style submit buttons by using JavaScript to change the submits to buttons and storing their value in a hidden field. It's one of the standard techniques for handling this particular problem. The problem is that it requires the form to have an ID so it can identify all the button within. Now, I know that according to web standards the form should have an ID anyway, but I've never been fond of having markup that isn't used. So as a purely theoretical exercise I decided to write a function to get around that.
The function uses recursive calls and element.parentNode to find the first parent of the calling element that is of type 'tag'. For example, I could do an onclick on a button with this function to find the form that contains the button. I wrote the function as an extension to the Element class so it can be attached to anything.
Here's how you could make a form button get the form it is in. This example would alert 'foo'.
<form id="foo">
<button name="finder" type="button" onclick="alert(this.parentTag('form').id);">Find my form!</button>
</form>
I also got bored and decided to add a second parameter to the function to find not the first matching parent element, but the topmost one in the document. I don't really see much use for it, but it was easy to add in. It wouldn't be difficult at all to add functionality to find the Nth parent up from the calling element either.
<div id="foo">
<div id="bar">
<div id="baz">
<div id="quux">
<a href="#" onclick="alert(this.parentTag('div'))">I print 'quux'</a>
<a href="#" onclick="alert(this.parentTag('div', true))">I print 'foo'</a>
</div>
</div>
</div>
</div>
Functional version of the above code (changed divs to fieldsets to stay within the example code):
Here's the actual function. Feel free to use it.
Element.prototype.parentTag = function(tag, find_top) {
var found_node; // Holds the current found node
// Checks for matching tags
var nodeMatch = function(tag, node) {
return (node.tagName.toLowerCase() == tag.toLowerCase());
}
var bubbleTagMatch = function(tag, cur_node) {
parent = cur_node.parentNode;
// If we've reached the top of the tree, get out of here
if (parent == document) {
return found_node;
}
if (find_top) {
if (nodeMatch(tag,parent)) {
found_node = parent;
}
return bubbleTagMatch(tag, parent);
}
else {
return (nodeMatch(tag, parent)) ? parent : bubbleTagMatch(tag, parent);
}
}
// Start the recursion with the calling tag
return bubbleTagMatch(tag, this);
}