1

I have a button on a form that when clicked adds an additional subsection to the form via ajax. Now I need to make it so the form automatically scrolls to the start of the newly added content.

I rewrote the ajax callback function so it returns an ajax command array instead of just the changed form data, and added an ajax_command_replace call in the array to still have my content replaced. This works and from the user side everything is unchanged.

Now I found out that I can add an ajax_command_invoke command to have a jQuery function called upon the DOM element I want to interact with. Unfortunately there didn't seem to be an in-built scrollTo jQuery function, so I had to add my own.

This function works when I call it via Firebug, but the ajax callback just gives me an error:

TypeError: $element[response.method] is undefined $element[response.method].apply($element, response.arguments);

So I guess my problem is that my custom jQuery module isn't actually added to jQuery at the time the callback is handled, but only after that?

Here's my configuration (sensitive data hidden with <...>:

Within the Drupal Module, my callback function:

function callback($form,&$form_state) {

  drupal_add_js(drupal_get_path('module', '<...>') . '/js/jquery_plugins.js');

  $commands = array();

  $commands[] = ajax_command_replace(NULL, drupal_render($form[<element>]));
  $commands[] = ajax_command_invoke('<...>', 'scrollTo', array(750));

  return array(
    '#type'     => 'ajax',
    '#commands' => $commands
  );

}

In the included file, my jQuery scrollTo function:

(function(jQuery) {
  jQuery.fn.scrollTo = function(data) {
    jQuery('html, body').animate({
      scrollTop: jQuery(this).offset().top
    }, data);
  };
})(jQuery);

Where should I include my jQuery module so the callback knows what I want from it when it's handled?

Update:

I had most of my help until that point from this similar discussion on Drupal.org. It seems to have worked for them.

  • We'll need to see the real ajax_command_invoke('<...>', that should be the name of a jQuery selector and can't possibly be sensitive information for obvious reasons (one of them being that you can just inspect the page source to see that information) – Clive May 14 '13 at 12:23
  • Not really sensitive, just unnecessary: $commands[] = ajax_command_invoke('#div-fahrt-' . ($form_state['values']['fahrten'] + 1), 'scrollTo', array(750)); I'm scrolling to the top of the newly added div. – Deathmic May 14 '13 at 12:27
  • Thanks...have you confirmed that ID is correct in the front end after the AJAX call? Drupal tends to add --n to the end of IDs when it does an AJAX replace. It might seem unnecessary but it's not... – Clive May 14 '13 at 12:29
  • Yes, the ID is correct. I extracted all the data with firebug and duplicated the call manually. At the time it tries it, the scrollTo is not defined. Once it's finished loading the very same call works. – Deathmic May 14 '13 at 12:31
  • Ah yes that makes sense, I'll put something in an answer – Clive May 14 '13 at 12:33
1

Using drupal_add_js() in an AJAX callback will (at best) have weird results. Come to think of it I'm not sure I've ever actually seen it work.

The simplest solution is to add the JS in the form function itself, that way it'll always be available regardless of the AJAX state of the page:

function MYMODULE_some_form($form, &$form_state) {
  // Moved from the AJAX callback to here.
  $form['#attached']['js'][drupal_get_path('module', '<...>') . '/js/jquery_plugins.js'] = array();

  // ... Rest of the form
}
  • Unfortunately I'm still getting the same issue: TypeError: $element[response.method] is undefined $element[response.method].apply($element, response.arguments); I'm at a JavaScript breakpoint right now testing the call: $element["hide"] comes up as function(), but $element["scrollTo"] is undefined. – Deathmic May 14 '13 at 12:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.