10

I have a basic FAPI button input that is #ajax enabled and that's working fine, but I want to add a JS "Are you sure?" confirmation popup when clicking the button before the code actually runs, and I'm not sure how to do that because FAPI's JS seems to be eating the click before I can get to it no matter what I do.

I've tried adding an inline onclick handler, like so:

$form['search_filters']['channels']['channel_delete_' . $channel->nid] = array(
  '#type' => 'button',
  '#name' => 'channel_delete_' . $channel->nid,
  '#value' => 'Delete',
  '#attributes' => array(
    'class' => array('confirm'),
    'onclick' => "return confirm('Are you sure you want to delete that?')"
  ),
  '#button_type' => 'no-submit',
  '#ajax' => array(
    'callback' => 'delete_channel_callback',
    'wrapper' => 'channel_container_' . $channel->nid
  ),
);

...which doesn't help, and I've also tried adding:

$('.confirm').click(function(e) {
  e.preventDefault();
  alert('Is this recognized')? // never runs
});

in my module's JS which is also ignored.

Any other ideas? Is there a way to add a submit handler to the top of the stack which Drupal #ajax will recognize?

0

8 Answers 8

15
+50

Drupal is a PHP framework, and it's amazing how much fancy AJAX stuff you can get using that and the FAPI. But it has it limits and for a use case like this, I would suggest that you use custom JavaScript. Also instead of using the usually JavaScript dialog, you could use jQuery UI to create a themable dialog instead.

Anyways the problem that you're facing is probably caused because you use AJAX on the submit button. Since you are using AJAX for the actual delete call, preventDefault and the like won't work.

What you would have to do is something like this. (This is not widely tested but should work.)

Drupal.behaviors.module = {
  attach: function() {

    var events = $('.confirm').data('events'); // Get the jQuery events.
    $('.confirm').unbind('click'); // Remove the click events.
    $('.confirm').click(function () {
      if (confirm('Are you sure you want to delete that?')) {
        $.each(events.click, function() {
          this.handler(); // Invoke the click handlers that was removed.
        });
      }
      // Prevent default action.
      return false;
    });
  }
}
4
  • I added also $('.confirm').unbind('mousedown'); Drupal listerns 'mousedown' event too.
    – milkovsky
    Nov 4, 2012 at 22:58
  • wont this break the button if the user cancels the confirm dialog then clicks again? (because $('.confirm').unbind('click'); )... or am i reading it wrong? sorry not familiar with unbind Oct 17, 2013 at 2:03
  • 1
    @ErichBSchulz Unbind is only called once, by the attach method which is called on load of the page.
    – googletorp
    Oct 17, 2013 at 20:30
  • gist.github.com/guoxiangke/0dffa7722a67f052c7ce Uncaught TypeError: Cannot read property 'click' of undefined?????? I have a form,in node/add/content-type,and I ajax it,use js validate May 4, 2014 at 9:23
5

The question is old, but I was also interested in it. In my opinion, the easiest way is to use the click event in your Ajax definition, because Drupal uses the mousedown event as default:

$form['submit'] = array(
  '#type' => 'submit',
  '#value' => t('some value'),
  '#attributes' => array(
    'class' => array('some-class'),
    ),
  '#ajax' => array(
    'event' => 'click',    //add this line!
    'callback' => 'some_callback',
    'wrapper' => 'some-wrapper',
  ),
);

Then you just need to add a .mousedown() event to your button in your Javascript file, because it is fired before the click event:

$('.some-class').mousedown(function() {
  //do something
});

If you still want your Ajax Request called with the mousedown event, you can use a custom event:

//change this line in the Ajax Defintion of your button
'event' => 'click',
//to
'event' => 'custom_event',

Then you can trigger this event in the .mousedown() event of your Javascript file:

$('.some-class').mousedown(function() {
  //do something
  $('.some-class').trigger('custom_event');
});

Both versions work!

1
  • Yeah! I like this one because it does what you want: gives you a handle on the Drupal off-the-shelf process that you can then call when you need it. Nb. I needed to have a preventDefault in my click handler to prevent normal (non-js) submission of form, still. Jun 15, 2015 at 16:39
4

confirming that Jeroen is fixing the googletorp's code.

However, i myself discovered that the evens of type "mousedown" have to be unbid and binded again. So the piece of code that worked for me is the following:

(function ($) {
  Drupal.behaviors.confirm = {
    attach: function(context, settings) {
      var events =  $('.form-submit-delete').clone(true).data('events');// Get the jQuery events.
      $('.form-submit-delete').unbind('mousedown'); // Remove the click events.
      $('.form-submit-delete').mousedown(function () {
    if (confirm('Are you sure you want to delete that?')) {
      $.each(events.mousedown, function() {
        this.handler(); // Invoke the mousedown handlers that was removed.
      });
    }
    // Prevent default action.
    return false;
      });
    }
  }
})(jQuery);
3
  • It's really a waste of resources to clone DOM objects unless you actually need them.
    – googletorp
    Feb 6, 2013 at 21:08
  • Hi,I met the same question, all goes well,but I met an js error:"Uncaught TypeError: Cannot read property 'click' of undefined" also for "mousedown". May 4, 2014 at 9:19
  • This is the only solution that I could get to work. Cloning is key here. Without the clone then when you click ok the alert just instantly reappears.
    – Felix Eve
    Jun 9, 2017 at 3:59
0

Have you tried running your JS inside of a Drupal behavior? It could just be that your click handler is being attached after Drupal's.

Either way though you should be able to unbind Drupal's click handler on the button so that you're called first and you can call Drupal's Ajax click handler manually (and conditionally).

0

Answering to note that googletorp's code is not 100% correct.

You need to change the line where the events are stored to this:

var events = $('.confirm').clone(true).data('events'); // Get the jQuery events.

This way you eliminate any object reference problems, so when unbinding the click events, it doesn't unbind in your var as well ;)

Also, be advised that since jQuery 1.8 the .data() method has been deprecated. Getting event data is now done via an internal data structure:

.data(“events”): jQuery stores its event-related data in a data object named (wait for it) events on each element. This is an internal data structure so in 1.8 this will be removed from the user data name space so it won’t conflict with items of the same name. jQuery’s event data can still be accessed via jQuery._data(element, "events") but be aware that this is an internal data structure that is undocumented and should not be modified.

Source: http://blog.jquery.com/2011/11/08/building-a-slimmer-jquery/

0

I found many useful answers above and was able to get my form working. Putting it all together, this is what worked for my application: A delete form button with a confirmation dialog and AJAX callback to modify the contents of a select list.

The javascript:

$form['my_form']['delete_button_js'] = array(
  '#markup' => '<script type="text/javascript">
  (function ($) {
     // override the default confirmation dialog behavior
     Drupal.behaviors.confirm = { 

      // Get the jQuery events.
      attach: function(context, settings) {
      var events =  $(\'.deleteButtonClass\').clone(true).data(\'events\'); 

      $(\'.deleteButtonClass\').unbind(\'mousedown\'); // Remove the mousedown event.

      $(\'.deleteButtonClass\').mousedown(function () { 
           // Popup the confirmation
           if (confirm(\'Are you sure you want to delete the Group?\')) {
             // if YES, then we fire the AJAX event 
             $(\'.deleteButtonClass\').trigger(\'deleteGroupEvent\'); 
           }
           // Override the default action.
           return false;
        });
    }
            }
  })(jQuery);
  </script>
  ',
);

The form element (select list) to be modified:

$form['my_form']['select_list'] = array(
         '#type' => 'select',
         '#title' => t('My Title'),
         '#options' =>  $optionsArray,
         '#size' => 5,
         '#prefix' => t('<div id="mySelectList">'),
         '#suffix' => t('</div>'),
    }

The button:

$form['my_form']['delete_button'] = array(
  '#type' => 'button',
  '#value' => 'Delete',

  '#attributes' => array( 
      'class' => array('deleteButtonClass'), 
    ),

  '#ajax' => array (

    // this is the custom event that will be fired from the jQuery function
    'event' => 'deleteGroupEvent', 

    'callback' => 'ajax_delete_callback',
    'wrapper' => 'mySelectList',
    ),

);

And finally, the AJAX callback:

function ajax_delete_callback ($form, $form_state) {

  // callback implementation code

  return $form['my_form']['select_list'];
}
-1

I have fixed this by modified the drupal misc/ajax.jx file.

Create a copy of ajax.js file and put it any custom module and modified the Drupal.ajax = function (base, element, element_settings) { function in beforeSend:

beforeSend: function (xmlhttprequest, options) {

    if( ajax.url =='/?q=field_collection/ajax'){ // condition for when click on the Remove button
      if (confirm('Are you sure you want to remove this?')) {
       ajax.ajaxing = true;
       return ajax.beforeSend(xmlhttprequest, options);
      }
      xmlhttprequest.abort();
    } else{
        ajax.ajaxing = true;
        return ajax.beforeSend(`xmlhttprequest`, options);   
     }    
},

Its working fine for me.

3
  • 1
    It counts as hacking core, and probably should be avoided.
    – Mołot
    May 12, 2014 at 12:01
  • 1
    And is completely unnecessary - en.wikipedia.org/wiki/Monkey_patch
    – Clive
    May 12, 2014 at 12:03
  • This was the only way I could get this concept to work w/o showing double confirm boxes. The one exception is I had to make the if statement check if ajax.url == 'field_collection_remove_js'. Maybe that's because of the version of Drupal I am using?
    – Richard
    Feb 3, 2017 at 13:58
-1

Maybe a bit old, but I am wondering why we cannot just use:

(function($, Drupal, window, document, undefined) {

Drupal.behaviors.oc_ua_pc = {
  attach: function(context, settings) {
    $('#button').click(function () {
      if(confirm('This action cannot be undone and you cannot change the form later. Do you want to submit?')) {
        return;
      }

      return false;
    });
  }
}
})(jQuery, Drupal, this, this.document);
3
  • Because Drupal binds on the mousedown event so your click event handler will never fire.
    – Felix Eve
    Jun 9, 2017 at 3:23
  • Drupal is not changing the way PHP or JS works.. Why would it NOT listen to the click event? Here Drupal is just used to attach the JS code. Once the site is loaded Drupal has done its job. JS listeners are all there, right? ;)
    – schlicki
    Jun 9, 2017 at 4:43
  • You raise a good question and to be honest I don't​ know why click event doesn't fire. However after extensive testing I can confirm that it doesn't. Probably need to have a read of Drupal's ajax.js to get a better understanding.
    – Felix Eve
    Jun 9, 2017 at 7:38

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.