Fillable HTML Forms
Introduction
The Fillable Forms page described how you can use DocOrigin Design to define input fields for use in HTML (or PDF). That is all within the DocOrigin domain. This page delves into the 'in-browser' side of fillable HTML forms. That means that it is mostly outside of the DocOrigin domain, except for when some DocOrigin provided facilities assist in your browser-side efforts. Mostly, it's up to you.
Within the hosting browser, HTML is wondrously dynamic. Via in-browser JavaScript and CSS you can create a very engaging, dynamic experience. But it is work.
Generally, we expect that for document generation purposes, that you will keep your interactive HTML forms quite simple -- to collect a small amount of data and to submit that data to the URL of your choice. If that is all you need to do then the declarative input field definitions you can make via DocOrigin Design are all you need. You would not have to enter into the in-browser world of things. That is our general premise, but you can do more, if you are up for it.
Supplying JavaScript
As always DocOrigin is very 'open'. You can choose to employ HTML-world application construction technologies -- e.g jQuery or other frameworks. Or you can try to keep your application slim. The door to this open world begins with the file Default-DOHtmlTemplate.htm, which resides in the ../DO/Bin folder. As usual, you can override that file by supplying a DOHtmlTemplate.htm (no Default- prefix) file in the .../User/Overides folder. The DOHtmlTemplate file is a very simple template which contains placeholders (e.g. *CONTENT*
) which DocOrigin Merge replaces with the HTML constructs that it generates. Another way for you to override the Default-DOHtmlTemplate.htm file is to supply the -HtmTemplate
option to Merge, it can specify a template file that you have modified, quite possibly to contain the inclusion of various JavaScript libraries (e.g. jquery) or custom CSS files.
-HtmTemplate $O/MySiteHtmTemplate.htm
Surely you would not vary this on a form-by-form basis but would build up a standard for your entire site.
The next, and more common, area where DocOrigin opens up to the browser world is on a per form basis. You can enter browser JavaScript, (as opposed to DocOrigin JavaScript with all of its extensions), into the "HTML Input Event" script area of the form object itself, via the Format-Form Properties... menu item. You may put JavaScript code directly into that "event" area, but more likely, especially during development, you would put in a #include "$$F/ThisForm.js"
construct, and then provide your browser JavaScript in that external file. If you are doing much application development you will likely have to edit it a lot <sigh> and it proves very convenient to have that editing done in an external text editor that keeps the file open, trial after trial.
<script>
element in the <head>
section of the generated HTML.Invoking your JavaScript
Now that you can supply JavaScript for your HTML form, how do you get it invoked? The most direct link is if you have supplied a viewer attribute on any of your input form objects of the style: onClick="MyFunction();"
. Thus, if you supply a function of that name in your JavaScript, it will be invoked.
The aforementioned DOHtmlTemplate.htm file includes a *FOOTER*
placeholder. That is normally replaced by the contents of file DOHtmlFooter.htm, which is also delivered in folder .../DO/Bin. That file is very well worthwhile looking at. For the topic immediately at hand you might discover in it that if you have provided a function named Initialize (note the capital I), then it will be called automatically as soon as the browser has loaded the form.
DocOrigin Assists
While 'in-browser' you are mostly outside of DocOrigin assistance, but some extra functionality is provided to you for your application-building efforts. Within the generated HTML, a DO object is created. It has some useful members, most particularly functions you may wish to call from your own JavaScipt. You are well advised to peruse the DOHtmlFooter.htm file to see what functions are defined within the DO object.
DO.submitForm([url])
is the most commonly used function. It does some vital housekeeping and then submits the data to the URL of your choice. Before that submit is done it will call a function named PreSubmit, if you have provided one. If that function returns false then the data submission will not occur.DO.makeVisible(...)
can be used to hide or show a pane based on dynamically determined relevance predicated on user inputs.DO.deletePane(...)
can be used to discard a pane entirely. It will no longer exist. Its data elements cannot be submitted.DO.addPane(...)
lets you add a sibling pane right after the pane that the parameters identify.DO.appendPane(...)
lets you append a sibling pane at the current end of the list of such panes. E.g. Add a pane for an additional dependent or coverage area.DO.clearPane(...)
clears all the input fields in a pane.
As these pane operations occur, the power of HTML dynamism is employed to squeeze the remaining panes together or spread them apart to make room, as necessary.
DO.message(text)
is useful for emitting messages to the user (or the developer!) without using an annoyingalert()
. If you were to use this in production, you would probably want to style the presentation to your own preferences.
There are a few other functions in there, though most others are 'internal'; more may be added for developer convenience, but we don't wish to bulk up the amount of JavaScript tagging along in every HTML document we generate.
Accessing Fields
This is not a tutorial on browser JavaScript. Naturally, document.getElementById() and document.getElementsByTagName("input") are your friends. HTML is open; you can use all of its features to accomplish your goals. However, some understanding of the construction of a DocOrigin HTML form could be useful.
Names and IDs
- Every HTML input element can have an ID and a name.
- If an input element does not have a name, it will not be submitted.
- It is the name property, not the ID, that is submitted along with its value.
- IDs are thought of as being unique, but with multiple pane instances, that is not the case.
- DocOrigin uses the input field's name, as per your design, as the ID.
- DocOrigin modifies the name dynamically to reflect the document structure. That name carries which pane it is in and also the instance of that pane.
So... getting an element by ID is fine, if it occurs only once, i.e. if it is in a "header" pane that ocurs only once. [Panes become <div>s in HTML.] But if a field occurs multiple times, then its ID will occur multiple times and you will have to take the appropriate steps to ensure that you fetch/update the correct element. It is quite possible that for the expected usage of collecting a small amount of information, rather than being a full-blown application, that the fields of interest will occur only once.
Remember, names change! A great way to get your bearings on the names is to, during development, submit your form to the http://docorigin.com/echo.php facility (or your copy of that on your own web server; echo.php is delivered, as an unsupported sample, under .../DO/Samples/Web). By using echo.php you will see the names of everything that was submitted. That is very instructive.
If you are going to do application development then you really should carefully review DOHtmlFooter.htm to see how it goes about renaming fields -- not necessarily to understand it fully but possibly to reuse some of the internal functions that it employs. Either that or develop your own functions for accessing multi-occurring IDs.
A PHP Wrinkle
PHP is reasonably popular on web servers. We use it in our example code. Alas, PHP does the nasty thing of translating dots in names to underscores. For that reason, we have chosen to use colons as name hierarchy separators and then, on the PHP side, we translate those colons back to dots.
DO submits: Page1:Pane1:GroupName:FieldName echo.php reports Page1.Pane1.GroupName.FieldName
Having underscores as hierarchy separators is useless since object names themselves can contain underscores. PHP provides no means to look at the raw data as submitted if that data submission is in multipart form encoded format (which it is for DO HTML submissions). Shame on you PHP.
php://input does not work for multipart format.
Checkboxes
Browsers have this annoying practice of not submitting anything if a checkbox is not checked. That is terrible. You really want the submitted/collected data to have a 0 or 1, but to always exist so that the data can be passed on to other applications.
DocOrigin solves that, at a price. It automatically provides a hidden field that is always submitted, and it automatically updates that hidden field whenever the checkbox is clicked. Sounds fine, right? Well, beware. You can get the value of the checkbox from that hidden field, which in fact has the ID of the field name you supplied in Design. However, to change that value you must set the checked property of the previous sibling. Consider a checkbox named, in Design, as Foo.
// Setting a checkbox to true var el = document.getElementById("Foo"); // The checkbox value, 0, or 1 is at el.value. el.previousSibling.checked = true; // That set the checkbox as being checked, but did not change the el.value el.value = "1"; // This occurs automatically when a user clicks on a checkbox. // But programmatically, you have more responsibilities.
Repeating: It is always good practice to use echo.php to see what is being submitted.
Radio Buttons
Browsers have this annoying practice of not submitting anything if a radio button is not checked. That is terrible. You really want the submitted/collected data to have a 0 or 1, for each button, so that data can be easily passed on to other applications.
DocOrigin solves that, at a price. Sound familiar? Each radio button is shadowed by a hidden field. All such hidden fields are updated whenever any radio button in the set is clicked. But let's work through an example.
Let's say that you are collecting the user's credit card of choice. You provide three radio buttons in the set: Amex, Visa, and Mastercard. As always, all the radio buttons in the set have to have the same name. Let's say we chose "CC" as that name. It's a nice idea, if possible, to put all of those radio button input fields into a group; quite likely naming the group CC as well.
Of course, you will immediately use echo.php to see what is submitted when you check Visa for example. You will see:
Page1:Pane1:CC:CC#1 = '0' Page1:Pane1:CC:$DO_CC = 'Visa' Page1:Pane1:CC:CC#2 = '1' Page1:Pane1:CC:CC#3 = '0'
What a bonanza! Each radio button named CC is represented by a name of CC#n
, where that n goes from 1 to however many radio buttons there are in the set. You get all of their individual settings. Additionally, you get a $DO_CC field which holds the value you defined for the chosen radio button, in the "Choice:" property of its input dialog.
This is great for downstream processing of the submitted data (and in a generic way). It permits the creation of XML that can be passed along and even be given to DocOrigin Merge to produce a new document, in HTML, PDF, or whatever.
The price. When you want the logical value of the radio button set named CC, you have to ask for $DO_CC
. But no, not even that works. That is because there are several radio button fields, each with the ID $DO_CC.
Only one gets submitted because that's the way that browsers choose to operate. They submit only the one that was checked.
What can you do? You have several standard browser JavaScript ways to get all the input fields, or all the radio button fields. (document.getElementsByTagName("input")
or document.getElementsByClassName('DO_radio')
-- all DocOrigin radio button fields get the class name DO_radio). Anyway, you can then run through that array of elements and look at their IDs. If the ID is $DO_CC
you can then check whether its checked property is true or false. If it is true, you can use its value property as the value of the radio button set. Surely, you will develop a function for that once and use it over and over.
That's fine for fetching the value. What about setting it? Just as for checkboxes it is all done automatically in reaction to user clicks but if you set a radio button to checked via JavaScript, then you have to take on the responsibility of updating the hidden shadow fields as well. As it happens there is a DO.prepRadio()
function which circles around the whole document and ensures that the hidden shadow fields for radio buttons each reflect their counterpart real radio button field's checked status.
Across Panes Radio Buttons
We like to think that most radio button sets will be inside a single pane and that you will take the effort to group those out of a sense of tidiness. However, fillable HTML forms, (and not fillable PDF forms), support the ability to string out a radio button set across many panes. Typically a recurring pane would have one radio button of the set in each instance of the pane (think of each pane as offering a radio button selectable choice to the user). Naturally, such radio buttons cannot be grouped. However, since the radio buttons do all have the same name (in Design), they will be treated as part of a radio button set. Operations will be as described in the foregoing paragraphs.
Multi-instance Context
Suppose that you have defined an OnClick event on an input field, and it invokes a function you provide. Great, but hey, what context are you in? If this field occurs in a pane that can occur multiple times, just which occurrence are you in? If you want to do an Amount = Price * Quantity calculation, you had better be using elements from the same instance of the pane that reacted to your OnClick event.
I'm sure that you can invent navigation algorithms that let you figure out where you are in the DOM, and then 'walk the DOM' appropriately to find your elements of interest. Getting the list of all input elements (document.getElementsByTagName("input")) and wandering that array looking for your this object might be one way. One thing that helps is employing a consistent ID'ing style -- i.e. the names that you provide in Design for pages, panes, and groups. That suggests another approach.
As you have seen, DocOrigin changes the names of fields dynamically to reflect the latest number of occurrences of each pane. It comes up with names such as:
Page1:Pane1#2:CC:CC#1
As it is programmatically produced, it is obviously consistent. If a pane occurs multiple times it has a #n
appended to that segment of the name. Those names are updated just before DO.submitForm()
does a submit. But we can steal some of that. Consider:
var form = document.forms[0]; for (var p=form.firstChild; p; p=p.nextSibling) { DO.renameFields(p, ""); // give fields an A.B#2.C type of structure }
Your code could do that. Then you could look at the .name property of the field that invoked your function (or whatever context you are in), and you would see the hierarchy of your object of interest. You have your context.
You could do just:
DO.renameFields(document.forms[0], "");
and then ignore the first segment of every name since it will always be the same -- the name of the form object itself. But you would have established your context. On a DO.submitForm
the fields will be renamed to their proper ready-for-submission text.
Group Visibility
Sometimes it is not only fields that are of interest. A typical case is that of a group of objects. You may wish to make them appear or disappear based on user interaction. Named-in-Design groups get IDs as well, so you can get their element object and set their .style.display
property as appropriate. Just standard browser JavaScript.
Pages / Tabs
By default, DocOrigin Merge puts each page of the generated HTML document into a tab whose name is the page name, plus the occurrence number if that page layout occurs multiple times. A function named DO.showPage(pageName)
can be used to switch focus to a specific tab, should you ever wish to wrest that decision away from your user.
A tabbed presentation of pages is not always wanted. You can control that with the command line option:
-HtmNavBar none
In which case the generated "pages" will be displayed as a single HTML page through which the user would scroll. The length of each heretofore page is trimmed of white space from the bottom so as to reduce the need for scrolling.
The opposite, and default, setting is:
-HtmNavBar tab.
Previewing Your HTML
DocOrigin Design offers a Tools-HTML Preview... menu item. If you don't want to change any options from your last preview, you can just hit F7. (You'll be using that a lot.)
What happens is that after Merge creates the HTML output, it directs it to a .bat file named Default-HtmlPreview.bat. Naturally, that is delivered in .../DO/Bin. You should read that .bat file! Note that it offers you the opportunity to supply your own .bat file in $O/HtmlPreview.bat. ($O is .../User/Overrides). If you read the .bat file, you will see that it makes reference to other sample .bat files that may help you in better testing your generated HTML.
The dialog that invokes Merge's generation of the HTML has a curious little field in its bottom left corner, labeled HTMLPreview.bat option:. You can put anything you like in there. It will be passed to your HTMLPreview.bat file as an optional third parameter. You can choose to interpret what you enter any way you like. The samples mentioned earlier use that field as a means to choose whether to preview in Chrome, Firefox, or IE. Getting your forms to look and function properly in all the popular browsers is a challenge. This provides a means to see what's up.