- From: Dave Raggett <dsr@w3.org>
- Date: Tue, 24 Oct 2006 13:05:58 +0100 (BST)
- To: www-forms@w3.org
A key goal for the forms-lite testbed [1] was to work on a wide variety of current web browsers. The next decision was whether to make use of the built-in submission mechanism or to come up with something else based upon XmlHttpRequest (Ajax). I found a couple of disadvantages for using Ajax. The first is that it is hard to fully emulate the conventional semantics for submit, where the current page is pushed onto the browser history and replaced by a new page sent by the server. The second is that you couldn't support file upload as part of the form's data. The Web API working group has recently published a new working draft for uploading files from web page scripts, but I don't believe it is widely supported as yet. I therefore decided to focus initially on using the built in support for form submission. The HTML forms data model can be described as a set of named forms where each form has a collection of named fields. The HTML DOM is (mostly) defined in the W3C DOM2 Recommendation [2] document.forms[0] is the first form You can access the field by its name, e.g. document.forms[0].x for the field named x Fields have properties, e.g. name, type, value, checked, readonly, and disabled. If there is more than one instance of a field with the same name then the "form.name" evaluates to an array of the instances. This is particularly pertinent to radio buttons (type is "radio"), but also applies to other types and this can be taken advantage of when you want to make use of repeating sets of fields. The value of a field is generally given by its value attribute with exceptions for radio buttons and checkboxes. For checkboxes the checked property is a boolean that indicates whether or not the checkbox is ticked. The field's value is only included in the submitted data if checked is true. For radio buttons, you need to iterate through the array of fields with the same name to find which one has checked equal to true. The value for this field is then included with the submitted data. If the disabled property is true then the field's value won't be included in the submitted data -- this overrides the checked property. When it comes to expressions provided by attribute values set on particular fields, then the form associated with the field provides a scope for resolving references to other fields. Forms-lite lets you write the following: <input name="x" type="number"/> <input name="y" type="number"/> <input name="z" type="number" calculate="x+y"/> where x, y and z are all part of the same form. The type of the field is important to how the expression is evaluated. In particular, ECMAScript defines the + operator to behave differently according to the types of its arguments. On strings it acts as a concatenator, whilst on numbers it adds them. ECMAScript also supports the notion of nested scopes. Thus locally defined variables take precedent over globally defined variables. Forms-lite takes advantage of this to allow you to refer to global variables and functions from within expressions. Names of fields for the current form take precedence over global variables. Forms-lite allows you to define a repeating set of fields. This is analogous to a set of rows in a spreadsheet, where the field name identifies the column, and a row index identifies the row. For expressions defined on fields within repeating set of fields, the scope is the current row, then the set of non repeating fields for the current form, and then the global scope. The expressions never include a row index. Thus in the above example, all three fields could be part of a repeating fieldset, and correspond to a set of rows, where in each row the value of z is the sum of x and y. Forms-lite also allows you to write expressions that are evaluated over a set of rows. So far I have implemented two such second order functions: count(name) Where name identifies a checkbox in a repeating set of fields. This function counts the number of ticked checkboxes in the column. sumover(name, expr) Where name identifies a repeating set of fields. This function sums the result of evaluating the expression expr over each row in the repeating set of fields. Forms-lite provides a mechanism for defining repeating sets of fields using the HTML fieldset element as a container for the set of fields to be repeated. The fieldset element defines a scope, although this isn't reflected in the HTML DOM directly. The count function described above should perhaps take the name of a fieldset and use that to restrict its scope to just the checkboxes contained by that fieldset. Repeating sets of fields and radio buttons don't mix well in the HTML DOM. This is because the group of radio buttons is represented in the DOM as an array of fields. This makes it impractical to include radio buttons in a repeated fieldset element. The work around is to use an HTML select element instead. Another issue with repeated fieldsets that are generated dynamically relates with how web browsers support the "back" and "reload" operations. When you move away from a page, the browser saves the current state of the form as part of the history. When you go back to that page, the browser restores that state based upon the page's markup. If a repeating set of fields is generated dynamically as a result of the page's onload event, only the values for the first row will be restored. Forms-lite deals with this problem by clearing the state of such repeating fieldsets. Ideally, the DOM would provide a means for the script to access the state recorded in the history as that would then allow the state of all rows to be restored. This is something that the Web API working group could perhaps be asked to provide as an extension to the DOM for the browser history. Some weird things I discovered: - On IE you need to use the htmlFor property when you want the the value of the for attribute on the label element - On IE you need to use the className property when you want to get or set the value of the class attribute. Luckily most other browsers also support className for this purpose - On Opera, you can't access the legend element within a fieldset element by traversing the DOM tree. You can however access it via calling getElementsByTagName("legend") on the fieldset. - Some browsers will fall back to the name attribute if the id attribute is mising on the input element when it comes to binding a label via its for attribute - Some browsers will include the fieldset element as part of the form when you provide a name attribute on the fieldset. - Opera coerces the value of the type attribute returned with getAttribute to "text" if Opera doesn't recognize the type. There needs to be a well defined interoperable way to identify whether a browser has a native implementation so that you can take advantage of that rather than relying on interpreting the attributes via a script library. Native implementations may have weaknesses, so that you may still want to use a script library to define the behavior, to ensure that end-users get the same behavior on all browsers. It would be desirable to have a well defined way to disable the native implementation in such cases. In HTML, the form element encloses the fields that belong to it. This is occasionally inconvenient, and it would be desirable to be able to instead use an attribute to reference the form by name. I have yet to experiment with this and to determine whether this can be made to work on existing browsers. It would involve inserting the fields into the form's object model via a script, but whether the submit mechanism would work in that case needs experimental verification. [1] http://people.w3.org/~dsr/forms-lite/ [2] http://www.w3.org/TR/DOM-Level-2-HTML Dave Raggett <dsr@w3.org> http://www.w3.org/People/Raggett
Received on Tuesday, 24 October 2006 12:06:15 UTC