Joseph K. Myers

Tuesday, June 4, 2002

Doing Things Like Auto-Fill

The main use of a computer is to "do the work" for us. Often we must type a long list of things which are identical, except for some minor change.

You can take DNS servers as an example.

Picture a form like this:

  ns1:
  ns2:
  ns3:
  ns4:
  ...
  ns13:

For the first address you might enter just about anything, but for the second, third, etc., you will likely enter exactly the same thing, except for a different number, i.e., ns1.something, ns2.something, and so on.

Entering even a few of these manually is prone to mistakes, and tends to make fingers sore. Thankfully, it is something the computer could be good for.

In order to change the numbers, we must first represent the address as a series of number parts and non-number parts, like: ns, 1, .something. As you see, the number is now separated, and we can easily substitute another number, and create new addresses.

JavaScript would do this as follows:

function ssum(s, n) {
	var p = s.match(/\D+|\d+/g)||[], i;
	for (i=0; i<p.length; i++)
		if (!isNaN(p[i]))
			p[i] -= -n;
	return p.join('');
}

In this "string sum" function, ssum, we do three things.

1. Split the string, s, into parts.

2. For all digit-only substrings add the number n.

3. Join and return the parts as a new string.

The first step we accomplish by matching all non-digit "parts" (\D+), and all digit-only parts (\d+), and placing them into p. An empty string will produce no parts (null), and so we will then use an empty array, [].

For the second step, we test each part in turn with the isNaN() function. Since every part of p is in the form of a string, we cannot add simply by saying p[i] += n. Instead, we may subtract the negative of n, which automatically converts the digit string to a number. (Requiring a variable to act as a certain type, in this case a number, is a process called "type coercion.")

There is nothing fancy about the third step- we merely create a new string by joining the parts of p.

Now, in order to implement this strategy into a real form, we will assume that the fields are named 'ns1', 'ns2', and so forth.

For each field except the first, we will request the previous field's value, and then auto-enter a new value consisting of the string-sum of the first and the number 1; that is, ssum(value, 1). We also remember that an existing value should not be overwritten.

So we have:

  <input type="text" name="ns1" />
  <input type="text" name="ns2"
	onfocus="if (!this.value) // no existing value.
		this.value = ssum(ns1.value, 1)" />

Of course, that might be a lot of code for you to type!- especially for twelve different fields. Since all of the functions are doing approximately the same thing, we can use the computer to simplify its own job.

We will write a generic function, ns_enter().

function ns_enter(field) {
	if (field.value)
		return;
	var old_field = field.form.elements[ssum(field.name, -1)];
	var new_value = ssum(old_field.value, 1);
	field.value = new_value;
	field.select();
}

In each input field beyond the first, we will now simply say:

  <input type="text" onfocus="ns_enter(this)" .../>

And since it was so easy, we also enhanced the function by causing it to select the new entry, making correction easier in case our guess was wrong. (You might have also noticed that the ssum() function was also used to get the previous field name!)

At last, here's what we've got:

<script type="text/javascript">

function ssum(s, n) {
	var p = s.match(/\D+|\d+/g)||[], i;
	for (i=0; i<p.length; i++)
		if (!isNaN(p[i]))
			p[i] -= -n;
	return p.join('');
}

function ns_enter(field) {
	if (field.value)
		return;
	var old_field = field.form.elements[ssum(field.name, -1)];
	var new_value = ssum(old_field.value, 1);
	field.value = new_value;
	field.select();
}

</script>

<form action="somewhere">

<p>Enter new nameservers.</p>

<pre>
  ns1: <input type="text" name="ns1" value="ns1.something.net" />
<script type="text/javascript">

for (var i=2; 14>i; i++) {
	document.writeln(
		'  ns' + i + ':',
		' <input type="text" name="ns' + i + '"',
		' onclick="ns_enter(this)" />'
	);
}
</script>
</pre>

</form>

This example was inspired by my own registrar, Go Daddy Software, http://registrar.godaddy.com/.