diff --git includes/ajax.inc includes/ajax.inc
index 808cefb..45b4156 100644
--- includes/ajax.inc
+++ includes/ajax.inc
@@ -253,10 +253,42 @@ function ajax_form_callback() {
     // Remove the value for form validation.
     unset($_REQUEST['ajax_triggering_element']);
   }
+  // To either validate or avoid validation we need to know whether this is
+  // a button or not. Grab the marker sent by js to see.
+  $triggered_by_button = TRUE;
+  if (!empty($_REQUEST['ajax_triggered_by_button'])) {
+    $triggered_by_button = ($_REQUEST['ajax_triggered_by_button'] == 'true');
+    // Remove the value for form validation.
+    unset($_REQUEST['ajax_triggered_by_button']);
+  }
+
   list($form, $form_state, $form_id, $form_build_id) = ajax_get_form();
 
+  // If this has been triggered by a button, we can safely validate
+  // with the normal form validation.
+  if ($triggered_by_button == TRUE) {
   // Build, validate and if possible, submit the form.
   drupal_process_form($form_id, $form, $form_state);
+  }
+  // However, if it was not triggered by a button, then there's no way
+  // to know how much of the form is ready to validate, and validation must
+  // not take place.
+  else {
+    $form_state['values'] = array();
+    // Build the form.
+    $form = form_builder($form_id, $form, $form_state);
+
+    // chx suggested the next line, but I suspect that we shouldn't do any
+    // validation at all when a non-submit element is clicked.
+    // drupal_validate_form($form_id, $form, $form_state);
+
+    // drupal_html_id() maintains a cache of element IDs it has seen,
+    // so it can prevent duplicates. We want to be sure we reset that
+    // cache when a form is processed, so scenarios that result in
+    // the form being built behind the scenes and again for the
+    // browser don't increment all the element IDs needlessly.
+    drupal_static_reset('drupal_html_id');
+  }
 
   // This call recreates the form relying solely on the $form_state that
   // drupal_process_form() set up.
diff --git misc/ajax.js misc/ajax.js
index ee56aad..8969954 100644
--- misc/ajax.js
+++ misc/ajax.js
@@ -203,7 +203,7 @@ Drupal.ajax.prototype.beforeSubmit = function (form_values, element, options) {
   // Server-side code needs to know what element triggered the call, so it can
   // find the #ajax binding.
   form_values.push({ name: 'ajax_triggering_element', value: this.formPath });
-
+  form_values.push({ name: 'ajax_triggered_by_button', value: this.button });
   // Insert progressbar or throbber.
   if (this.progress.type == 'bar') {
     var progressBar = new Drupal.progressBar('ajax-progress-' + this.element.id, eval(this.progress.update_callback), this.progress.method, eval(this.progress.error_callback));
