1

I'm trying to load a node containing an ajax webform and add it to an existing div, this works fine, but when I click to next step, the form is submitted to the webform action path.

Any idea?

Thanks!

          $.ajax({
            url: '/path/to/my/node/with/webform'
            success: function (data) {
              // Add html to existing container.
              $(data).appendTo('body');
            }
          });

1 Answer 1

2

Short answer: you need to attach the Drupal behaviors after inserting your new content into the DOM:

$.ajax({
  url: '/path/to/my/node/with/webform'
  success: function (data) {
    // Add html to existing container.
    $(data).appendTo('body');
    // Attach the new behaviors to the DOM
    Drupal.attachBehaviors();
  }
});

Long answer: Although Drupal has jQuery included with core, it does not use jQuery functions like $.on() or $.delegate() or $.live(), which attach behaviors to elements when they are inserted into the DOM. Instead Drupal uses behaviors, which is a Drupal specific method of attaching behaviors to elements.

Behaviors have three parts:

1) Scripts add new behaviors to the Drupal.behaviors object. Example:

Drupal.behaviors.someArbitraryKey = {
  attach: function (context, settings) {
    attachHandlerToElement(context, settings);
  }
};

Generally people consider this to be an onload function, as it is called when the document has loaded and is ready to have JavaScript executed on it. However, it can also be called using Drupal.attachBehaviors(), which will be addressed in #3 below. When the DOM is loaded, or Drupal.attachBehaviors() is called, the .attach() function of the behavior is executed, in this case calling attachHandlerToElement(context, settings); whenever this is executed.

2) $.once() is used to ensure that handlers are not attached multiple times to an element. Because behaviors can be executed multiple times, event handlers can be attached to elements multiple times, if steps aren't taken to prevent that. The jQuery $.once() function, provided by core, ensures that the code it contains is only attached to the element a single time:

function attachHandlerToElement(context) {        
  $(context).find(".some_selector").once("some-arbitrary-key").each(function () {
    // Note that $(this) is the current iteration
    // of .some_selector
    $(this).click(function () {
      // Do something.
    });
  });
}

By calling $.once() on elements, the contained code in the $.each() loop is only ever attached a single time. This means that the $.click() handler above is only attached a single time to elements with the class .some_selector.

3) When new content is inserted into the DOM, whether that be from an AJAX call, or even just new elements inserted using JavaScript, Drupal.attachBehaviors() is called. This in turn loops through all behaviors, and executes their attach() handler.

A final note on context. On initial page load, context is the full DOM. Drupal.attachBehaviors(context) takes an argument, context, which should be an element of the DOM (not a jQuery object containing it). In your case, you are appending your content to the body, so there is no need to pass an argument, as you will need to parse the full DOM. If you were appending your content to some element though, you could do something like this:

$(data).appendTo('#some_element');
Drupal.attachBehaviors($(#some_element").eq(0));

This would pass it the element of the DOM to which the new content was attached. This element becomes context in the attach() handler, and is more efficient than passing in the full DOM.

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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