Skip to content

Commit

Permalink
named form element lookup, part 1. This actually works, but is kinda …
Browse files Browse the repository at this point in the history
…ugly. Part 2 will have more tests and be less hacky
  • Loading branch information
client9 committed Apr 9, 2010
1 parent 5710074 commit ea6f131
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 11 deletions.
37 changes: 37 additions & 0 deletions src/html/element.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,24 +167,59 @@ __extend__(HTMLElement.prototype, {
/**
* Named Element Support
*/

/**
* Not all children of a form are named elements
* returns the parent form element or null if
* there is no parent form or if not a named element
*/
_isFormNamedElement: function(node) {
if (node.nodeType === Node.ELEMENT_NODE) {
switch (node.nodeName.toLowerCase()) {
case 'button':
case 'fieldset':
case 'input':
case 'keygen':
case 'select':
case 'output':
case 'select':
case 'textarea':
return true;
}
}
return false;
},
_updateFormForNamedElement: function() {
if (this._isFormNamedElement(this)) {
if (this.form) {
// to check for ID or NAME attribute too
// not, then nothing to do
this.form._updateElements();
}
}
},
setAttribute: function(name, value) {
var result = Element.prototype.setAttribute.apply(this, arguments);
this.ownerDocument._addNamedMap(this);
this._updateFormForNamedElement();
return result;
},
setAttributeNS: function(namespaceURI, name, value) {
var result = Element.prototype.setAttributeNS.apply(this, arguments);
this.ownerDocument._addNamedMap(this);
this._updateFormForNamedElement();
return result;
},
setAttributeNode: function(newnode) {
var result = Element.prototype.setAttributeNode.apply(this, arguments);
this.ownerDocument._addNamedMap(this);
this._updateFormForNamedElement();
return result;
},
setAttributeNodeNS: function(newnode) {
var result = Element.prototype.setAttributeNodeNS.apply(this, arguments);
this.ownerDocument._addNamedMap(this);
this._updateFormForNamedElement();
return result;
},
removeAttribute: function(name) {
Expand All @@ -206,6 +241,7 @@ __extend__(HTMLElement.prototype, {
importNode: function(othernode, deep) {
var newnode = Element.prototype.importNode.apply(this, arguments);
this.ownerDocument._addNamedMap(newnode);
this._updateFormForNamedElement(newnode);
return newnode;
},

Expand All @@ -214,6 +250,7 @@ __extend__(HTMLElement.prototype, {
var newnode = Element.prototype.replaceNode.apply(this, arguments);
this.ownerDocument._removeNamedMap(oldchild);
this.ownerDocument._addNamedMap(newnode);
this._updateFormForNamedElement(newnode);
return newnode;
}
});
47 changes: 39 additions & 8 deletions src/html/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
HTMLFormElement = function(ownerDocument){
HTMLElement.apply(this, arguments);

//TODO: on __elementPopped__ from the parser
// we need to determine all the forms default
// values
Expand All @@ -25,20 +26,12 @@ __extend__(HTMLFormElement.prototype,{
this.setAttribute('action', action);
},

// returns HTMLFormControlsCollection
// http://dev.w3.org/html5/spec/Overview.html#dom-form-elements
get elements() {
return this.getElementsByTagName("*");
},
get enctype() {
return this.getAttribute('enctype');
},
set enctype(enctype) {
this.setAttribute('enctype', enctype);
},
get length() {
return this.elements.length;
},
get method() {
return this.getAttribute('method');
},
Expand All @@ -57,6 +50,44 @@ __extend__(HTMLFormElement.prototype,{
set target(val) {
return this.setAttribute("target",val);
},

/**
* "Named Elements"
*
*/
/**
* returns HTMLFormControlsCollection
* http://dev.w3.org/html5/spec/Overview.html#dom-form-elements
*
* button fieldset input keygen object output select textarea
*/
get elements() {
var nodes = this.getElementsByTagName('*');
var alist = [];
var i;
for (i = 0; i < nodes.length; ++i) {
if (HTMLFormElement.prototype._isFormNamedElement(nodes[i])) {
alist.push(nodes[i]);
this[i] = nodes[i];
if ('name' in nodes[i]) {
this[nodes[i].name] = nodes[i];
}
}
}
return new HTMLCollection(alist);
},
_updateElements: function() {
this.elements;
},
get length() {
return this.elements.length;
},
item: function(idx) {
return this.elements[idx];
},
namedItem: function(aname) {
return this.elements[aname];
},
toString: function() {
return '[object HTMLFormElement]';
},
Expand Down
40 changes: 37 additions & 3 deletions test/specs/window/spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -518,9 +518,7 @@ test('window.[atob|btoa]', function(){
/**
* Not sure where this goes, since it needs the parser
*/
test('Named Element Lookup', function(){
//one of the easiest way to test the HTMLParser is using frames and
//writing the document directly
test('Document Named Element Lookup', function(){
expect(4);
var iframe = document.createElement("iframe");
var doc;
Expand All @@ -546,3 +544,39 @@ test('Named Element Lookup', function(){
node2 = doc.foo;
ok(! node2, 'old named element is gone');
});

test('Form Named Element Lookup', function(){
expect(7);
var iframe = document.createElement("iframe");
var doc;

document.body.appendChild(iframe);
doc = iframe.contentDocument;
doc.open();
doc.write('<html><head></head><body><form name="foo"><div></div></form></body></html>');
doc.close();

var form = doc.foo;
var elements = form.elements;
ok(elements instanceof HTMLCollection, "form.elements is an HTMLCollection");
equals(elements.length, 0, "form.elements does not include non-form elements");
equals(form.length, 0, "form.length is 0");


// ok now let's try to use innerHTML
var str = '<form name="bar"><input name="input1"/></form>';
doc.body.innerHTML = str;
form = doc.bar;
elements = doc.bar.elements;
equals(elements.length, 1);
equals(form.length, 1);
print('element is : ' + elements.input1);
ok(elements.input1 instanceof HTMLInputElement);
ok(form.input1 instanceof HTMLInputElement);

/*
// the other one should be zapped
node2 = doc.foo;
ok(! node2, 'old named element is gone');
*/
});

3 comments on commit ea6f131

@dperini
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should "label" and "option" elements be considered form elements too ? As it is for "fieldset" ...

They both have a ".form" property like others, though, not sure this entitles them as form elements !

@client9
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi, they certainly are form elements, but the spec says you can't access them directly via document.a_form.a_element.

option element can be accessed by first finding the SELECT tag, then doing a lookup, like so:

document.a_form.a_select.an_option.

However that is not yet done (soon I hope).

Let me know if you think I miss read the spec. OR even better is to just making a tiny HTML page, and a small script to see how it works. (hey those could even be unit tests too!)

--nickg

@dperini
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh... sure, I see now what you need to do... No problem, you are correct. Thank you for the clarification !

Please sign in to comment.