Drupal 7 Ajax Form, add elements dynamically to a fieldset I have the following form:

  $form['#tree'] = TRUE;

  $tasks = array(
    'Julia',
    'John',
  );

  foreach ($tasks as $task_key =&gt; $task) {

    $form['task_fieldset'][$task_key] = array(
      '#type' =&gt; 'fieldset',
      '#title' =&gt; $task,
      '#prefix' =&gt; '&lt;div id="task-' . $task_key . '-fieldset-wrapper"&gt;',
      '#suffix' =&gt; '&lt;/div&gt;',
    );

    if (empty($form_state['num_todos'][$task_key])) {
      $form_state['num_todos'][$task_key] = 1;
    }

    for ($i = 0; $i &lt; $form_state['num_todos'][$task_key]; $i++) {
      $form['task_fieldset'][$task_key]['todo'][$i] = array(
        '#type' =&gt; 'textfield',
        '#title' =&gt; t('Todo'),
      );
    }

    $form['task_fieldset'][$task_key]['add_todo'] = array(
      '#type' =&gt; 'submit',
      '#name' =&gt; 'add_todo_' . $task_key,
      '#value' =&gt; t('Add more'),
      '#submit' =&gt; array('custom_mod_example_ajax_collection_add_todo'),
      '#ajax' =&gt; array(
        'callback' =&gt; 'custom_mod_example_ajax_collection_callback',
        'wrapper' =&gt; 'task-' . $task_key . '-fieldset-wrapper',
      ),
    );

  }

  return $form;


These are the callbacks:

function custom_mod_example_ajax_collection_callback($form, $form_state) {
  $name = $form_state['triggering_element']['#name'];

  $task_key = (int) substr($name, strpos($name, "_") + 1);

  return $form['task_fieldset'][$task_key];
}

function custom_mod_example_ajax_collection_add_todo($form, &amp;$form_state) {
  $name = $form_state['triggering_element']['#name'];

  $task_key = (int) substr($name, strpos($name, "_") + 1);

  $form_state['num_todos'][$task_key]++;
  $form_state['rebuild'] = TRUE;
}


And the form looks like this: http://imgur.com/a/ZJ0ky
The problem is that when I click on "Add more" on John fieldset, it changes to Julia. What have I done wrong?

It works as expected when I try Add more on Julia, however.
The problem was in the AJAX callback, and specifically the code line:

$task_key = (int) substr($name, strpos($name, "_") + 1);


The buttons are named add_todo_key, and the code above returns todo_key, and not the key.

It was solved simply by using strrpos, which find the position of the LAST occurrence of a substring, rather than strpos that finds the first.