Show and hide form element based on values of other form elements $form['bookpage'] should be hidden at first. Then should be shown based on user selected value in $form['student']. 

But somehow, before user selects anything, upon loading the option list, it already triggers the $form['student']'s ajax function and shows the $form['bookpage']. 

Why is this so?

P.S. _lms_user_get_students will return following array:


  array('0'=>'----', 'all'=>'All', '2456'=>'Student A', '2462'=>'Student
  B')


 $form['student'] = array(
        '#type' =&gt; 'select',
        '#title' =&gt; t('Student'),
        '#options' =&gt; (!isset($form_state['values']['book']) || $form_state['values']['book']==NULL)?array('0'=&gt;'----'):_lms_user_get_students($form_state['values']['class']),
        '#prefix' =&gt; "&lt;div id='student_div'&gt;",
        '#suffix' =&gt; "&lt;/div&gt;",
        '#id' =&gt; 'select-student',
        '#ajax' =&gt; array(
            'callback' =&gt; 'ajax_student_select_callback',
            'event' =&gt; 'change',
            'wrapper' =&gt; 'statistic_table_div',
            'method' =&gt; 'replace',
            'effect' =&gt; 'fade'
       ),
   );


  $form['bookpage_toggle'] = array(
        '#type' =&gt; 'hidden',
        '#value' =&gt; 'off'
  );     
$form['bookpage'] = array(
            '#type' =&gt; 'select',
            '#title' =&gt; t('Page'),
            '#options' =&gt; (!isset($form_state['values']['book']) || $form_state['values']['book']==NULL)?array('0'=&gt;'----'):_lms_user_get_bookpages($form_state['values']['book']),
            '#prefix' =&gt; "&lt;div id='bookpage_div'&gt;",
            '#suffix' =&gt; "&lt;/div&gt;",
            //'#disabled' =&gt; TRUE,

            '#states' =&gt; array(
                'visible' =&gt; array(
                    ':input[name="bookpage_toggle"]' =&gt; array('value' =&gt; 'on'),
                ),
                'invisible' =&gt; array(
                    ':input[name="bookpage_toggle"]' =&gt; array('value' =&gt; 'off'),
                ),
            )
       );


    function ajax_student_select_callback($form, &amp;$form_state) {

        //show page drop 
        if($form['student']['#value'] == 'all') $form['bookpage_toggle']['#value'] = "on";
}
Why the in-between form element? You can use the states API to check for a value in a select as well. Just check the student form element and it's value all.

$form['bookpage'] = array(
  ...

  '#states' =&gt; array(
    'visible' =&gt; array(
      ':input[name="student"]' =&gt; array('value' =&gt; 'all'),
      // Use multiple lines here for an AND condition. eg:
      // ':input[name="other-field"]' =&gt; array('value' =&gt; 'all'),
    ),
  )
);


This way you can remove the AJAX callback, and the bookpage_toggle element.

If you want to support multiple conditions, you can wrap the jQuery selectors in another array  to function as an OR:

$form['bookpage'] = array(
  ...

  '#states' =&gt; array(
    'visible' =&gt; array(
      array(':input[name="student"]' =&gt; array('value' =&gt; 'all')),
      array(':input[name="student"]' =&gt; array('value' =&gt; 'other value')),
    ),
  )
);


Finally if you want exclusive OR (XOR) you can do it like this to toggle on one of the elements, but never both of them:

$form['bookpage'] = array(
  ...

  '#states' =&gt; array(
    'visible' =&gt; array(
      'xor',
      array(':input[name="student"]' =&gt; array('value' =&gt; 'all')),
      array(':input[name="student"]' =&gt; array('value' =&gt; 'other value')),
    ),
  )
);


See this comment for AND, OR and XOR and a combination of those.