Reset a form except one field with Javascript

I had been working on an answer to this question, which is similar, and has now been marked as a duplicate. That question was using jQuery, so the answer would have been much shorter, leveraging things like event.data, detach(), etc. Below is the general algorithm, and a vanilla version of the approach I would take to handle not just this question's but also more complex scenarios where you want to exclude an entire subsection of a form.


The algorithm

  1. Detach the element from it's parent
  2. Allow the default reset() behavior
  3. Reattach the element in the correct location within the DOM

Here is a very simple translation of that plan in to code. This doesn't actually re-attach the element in the right location within the DOM. Please see the code snippet for a fully functional example.

// within the form's onreset handler, which fires
// before the form's children are actually reset

var form = document.getElementById("myForm"),
    exclude = document.getElementById("_01"),
    parentNode = exclude.parentNode;

parentNode.removeChild(exclude);

// use a timeout to allow the default reset() behavior
// before re-attaching the element
setTimeout(function() { parentNode.appendChild(exclude); });

NOTE the id change to _01, and refer to the "Side note" at the end of this answer for more info on that.


Important properties and methods

  • .parentNode
  • .nextSibling
  • .removeChild()
  • .appendChild()
  • .insertBefore()

var dataKey = 'data-exclude-selector';

function initiateReset(e) {
  e = e || window.event;

  var button = e.target,
    form = button.form,
    excludeSelector = button.getAttribute(dataKey);

  form.setAttribute(dataKey, excludeSelector);
}

function beforeReset(e) {
  e = e || window.event;

  var form = e.target,
    excludeSelector = form.getAttribute(dataKey),
    elements = form.querySelectorAll(excludeSelector),
    parents = [],
    siblings = [],
    len = elements.length,
    i, e, p, s;

  // When reset #5 is clicked, note the excludeSelector's value is js escaped:
  // #\0030\0035 element attribute value becomes #\\0030\\0035 as js var value

  for (i = 0; i < len; i++) {
    el = elements[i];
    parents.push(p = el.parentNode);
    siblings.push(s = el.nextSibling);
    p.removeChild(el);
  }

  setTimeout(function() {
    for (var j = 0; j < len; j++) {
      if (siblings[j]) {
        parents[j].insertBefore(elements[j], siblings[j]);
      } else {
        parents[j].appendChild(elements[j]);
      }
    }
  });
}
<form id="myForm" onreset="beforeReset()" data-exclude-selector="">
  <input id="_01" type="text" placeholder="clear" />
  <br />
  <input id="_02" type="text" placeholder="clear" />
  <br />
  <input id="_03" type="text" placeholder="clear" />
  <br />
  <input id="_04" type="text" placeholder="clear" />
  <br />
  <input id="05" type="text" placeholder="clear" />
</form>
<input value="Reset 1" type="reset" form="myForm" data-exclude-selector="#_01" onclick="initiateReset()" />
<input value="Reset 2" type="reset" form="myForm" data-exclude-selector="#_02" onclick="initiateReset()" />
<input value="Reset 3" type="reset" form="myForm" data-exclude-selector="#_03" onclick="initiateReset()" />
<input value="Reset 4" type="reset" form="myForm" data-exclude-selector="#_04" onclick="initiateReset()" />
<input value="Reset funky ID (05)" type="reset" form="myForm" data-exclude-selector="#\0030\0035" onclick="initiateReset()" />
<br/>&nbsp;
<br />
<hr/>
<br/>
<form id="complexForm" onreset="beforeReset()" data-exclude-selector="">
  <input class="exclude" type="text" placeholder="clear" />
  <br />
  <input class="exclude" type="text" placeholder="clear" />
  <br />
  <input type="text" placeholder="clear" />
  <br />
  <input type="text" placeholder="clear" />
  <br />
  <div class="childTest">
    <input type="text" placeholder="clear" />
    <br />
    <input type="text" placeholder="clear" />
    <div class="nestedTest">
      <input type="text" placeholder="clear" />
      <br />
      <input type="text" placeholder="clear" />
    </div>
  </div>
</form>
<input value="Exclude by class" type="reset" form="complexForm" data-exclude-selector=".exclude" onclick="initiateReset()" />
<input value="Exclude subsection" type="reset" form="complexForm" data-exclude-selector=".childTest" onclick="initiateReset()" />

Additional work

  • More work will need to be done to handle the case where one would want to allow reset on certain children of excluded nodes, but I imagine this could be handled in multiple different ways with a little thought

    1. a recursive version of this idea
    2. @Quentin's idea could be extended using cloneNode() to make a copy of the entire node, instead of detaching it, allow a full reset, then implement a mechanism to determine which portions of the clone to systematically restore

Side note (...rant?)

  • Although the HTML5 id attribute allows for 01 as valid, the spec does go on to indicate that it can be used for other purposes.

3.2.5.1 The id attribute

The id attribute specifies its element's unique identifier (ID). [DOM]

The value must be unique amongst all the IDs in the element's home subtree and must contain at least one character. The value must not contain any space characters.

Note: There are no other restrictions on what form an ID can take; in particular, IDs can consist of just digits, start with a digit, start with an underscore, consist of just punctuation, etc.

Note: An element's unique identifier can be used for a variety of purposes, most notably as a way to link to specific parts of a document using fragment identifiers, as a way to target an element when scripting, and as a way to style a specific element from CSS.

This may not be a problem for you, but it is something to be aware of. For instance, document.querySelector[All]() uses CSS style selectors.

elementList = document.querySelectorAll(selectors);

...

  • selectors is a string containing one or more CSS selectors separated by commas.

According to the latest draft of the CSS Selectors spec

An ID selector contains a "number sign" (U+0023, #) immediately followed by the ID value, which must be an CSS identifiers.

And at the end of the rabbit hole are the rules for CSS identifiers

  • In CSS, identifiers (including element names, classes, and IDs in selectors) can contain only the characters [a-zA-Z0-9] and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_); they cannot start with a digit, two hyphens, or a hyphen followed by a digit. Identifiers can also contain escaped characters and any ISO 10646 character as a numeric code (see next item). For instance, the identifier "B&W?" may be written as "B\&W\?" or "B\26 W\3F".

    Note that Unicode is code-by-code equivalent to ISO 10646 (see [UNICODE] and [ISO10646]).

So if you're only using document.getElementById() you might be ok with an element id value like 01, but in general I would avoid it. Yes, with document.querySelector[All](), and any other component that uses CSS style selectors you may be able to get around this limitation by escaping the selector correctly, but this is a pitfall waiting to happen, especially if multiple developers are involved. I've included an example (5th reset button) in the code snippet for completion, if you have to interact with elements that have IDs taking a format like this.

Reference table for hex codes


Copy its value to a variable. Reset the form. Reassign the variable back to the value.