1

This is simplified code to exactly reproduce a problem I'm having with jQuery.

I would expect it to cancel the original click, then trigger the click event again which in turn would cause the browser to load the href of the url.

<script type="text/javascript">
$( function()
{
    var confirmed = false;
    $('a').click( function( event )
    {
        if ( confirmed == false )
        {
            event.preventDefault();
            confirmed = true;
            $(event.target).trigger( 'click' );
        }
    });
});

</script>

Original Question Here

I do not want to change window.location. I want to trigger so that any event handlers that have been bound to the click event will fire too. It just so happens that I also want the browser to follow the link.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Tim G
  • 1,812
  • 12
  • 25
  • This should create recursion. `$(this).trigger('click')` will call the handler. You can simply remove the handler and the link will work as expected. – N Rohler Dec 05 '11 at 16:58
  • 1
    What are you actually trying to achieve here ? only allow a link to be clickable on second click ? – Manse Dec 05 '11 at 17:00
  • @ManseUK - no, i'm trying to do a prevent default, then use the jQuery trigger method to programmatically trigger a click on the anchor element. – Tim G Dec 05 '11 at 17:43
  • @TimG - `preventDefault` prevents the browser default and will still run any code after the `preventDefault` line - just put your code in there ? – Manse Dec 05 '11 at 17:46
  • @ManseUK This is a very simplified script to reproduce an issue I'm having in a plugin I'm writing. I can't just put my code after the prevent default. – Tim G Dec 05 '11 at 18:34
  • I went through the original code that you posted. Can you make the variable `confirmed` scope global ? (initializing it outside the function just as the `pluginName` in your original code) – Shantha Kumara Dec 05 '11 at 17:20
  • not possible - needs to be held per element. – Tim G Dec 05 '11 at 17:23
  • I really don't understand your question. You say "I want to trigger so that any event handlers that have been bound to the click event will fire too." - why wouldn't they? You also say "I do not want to change window.location", but then you say "I also want the browser to follow the link." The way I read your code is: The link gets clicked, all listeners the element has get called; If variable confirmed is not true, set it true and call all listeners the element has again all over again. What does this achieve? – kontur Dec 16 '11 at 15:05

6 Answers6

2

Your event handler will always be run when you trigger a "click" event, and before the default behavior happens. So no, it's not a bug.

edit — If you want the default action to happen when the element is clicked and your various conditions are satisfied, just return from your handler.

$('a').click( function( event )
{
    if ( confirmed == false )
    {
        event.preventDefault();
        confirmed = true;
    }
    else
        return;
});

When your handler returns, the browser will carry on with the default behavior.

edit again — and of course if you want to simply carry out programmatically the default action of an <a> tag with an "href" attribute, it's just:

   window.location = $(element).attr('href');

No need for a "click" event at all.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • [demo](http://jsfiddle.net/hEKBs/4/) this does not work. Furthermore, I want to trigger the click so that it works with just 1 click. It's obtuse I know, but if you reference the latest edit I made with a link to another question I've asked about this issue, you'll see the entire scope of my problem. thanks. – Tim G Dec 05 '11 at 17:09
  • @TimG You're not doing the same thing in that fiddle as in my answer above. There's no need to trigger another click if you're already handling one! – Pointy Dec 05 '11 at 17:17
  • but I *need* to trigger. Reference the [full plugin](http://stackoverflow.com/q/8372110/608781) I'm writing. – Tim G Dec 05 '11 at 17:18
  • @TimG answer updated - you still don't need to trigger an event. :-) – Pointy Dec 05 '11 at 17:20
  • well, that's the answer I received to my original question. I was really hoping to find out if it's a jQuery bug or simply that I don't understand how jQuery event handling works in this scenario. thanks for your efforts though - appreciated. – Tim G Dec 05 '11 at 17:22
  • I do need to trigger click in case other code has bound event handlers to click. – Tim G Dec 16 '11 at 06:57
  • @TimG If other event handlers are bound, they'll be invoked anyway if you don't call "stopImmediatePropagation()" on the event object. In fact they may have run before getting to this handler; it's hard to control that. – Pointy Dec 16 '11 at 15:18
2

.trigger('click') won’t trigger the browser’s default action — it will just trigger jQuery event handlers bound to that event on that element.

Take a look at this answer — you need to create and fire a click event yourself. Here’s the code from that answer:

function fireEvent(obj,evt){
    var fireOnThis = obj;
    if( document.createEvent ) {
      var evObj = document.createEvent('MouseEvents');
      evObj.initEvent( evt, true, false );
      fireOnThis.dispatchEvent( evObj );

    } else if( document.createEventObject ) {
      var evObj = document.createEventObject();
      fireOnThis.fireEvent( 'on' + evt, evObj );
    }
}
Community
  • 1
  • 1
s4y
  • 50,525
  • 12
  • 70
  • 98
0

preventDefault() doesn't cancel the jquery bound click action; it cancels the 'default' click action that is inherent to an anchor tag. unbind() is the proper function for canceling any action/function that is bound to an object.

Ecropolis
  • 2,005
  • 2
  • 22
  • 27
0

It looks like you have one state where you want to inhibit the default action (and update a flag), and another state where you simply want the default action to apply. Wouldn't removing the else case entirely do the trick? With no preventDefault() the default click action will still run.

Edit: Pointy's answer now shows your code updated similarly.


Edit: I'm still not entirely sure what you're going for, but if for some reason you must trigger manually, one way to prevent a stack overflow due to recursion would be to assign a custom event (i.e., not click), and then always suppress the default action. Then in your conditional, either do or don't fire the custom event.

So, something like this (untested):

$('a')
  .bind('fancyclick', function () { // Your special click logic
    changeStatusOf( confirmed );
    doCrazyStuff();
    location.replace( $(this).attr('href') ); // or whatever
  })
  .click( function (e) {
    e.preventDefault(); // Always suppress natural click
    if ( confirmed ){
      $(this).trigger('fancyclick'); // Fire the special click
    }
  });
Ken Redler
  • 23,863
  • 8
  • 57
  • 69
  • @TimG, Right -- @Pointy is just skipping the `preventDefault()` if `confirmed` is `true`. No need to trigger because the click will fire "naturally" when the function returns. – Ken Redler Dec 05 '11 at 17:12
  • I need to trigger, not just skip the preventDefault on a second mouse click. – Tim G Dec 05 '11 at 17:14
  • @TimG, I've updated my answer. Still not certain what you're looking for, but maybe this helps. – Ken Redler Dec 05 '11 at 17:18
  • the issue isn't about recursion - it's about the call to `.trigger( 'click' )` not actually "clicking" the anchor element – Tim G Dec 05 '11 at 17:20
0

I'll simply write while I analyse, hoping it'll be easy to follow and easy to compare to your code. Kick me when I can optimize/enhance my replies in any way.

checking - part 1

Move the "preventDefault" outside of the "if" statement! If you don't move it outside of the "if", the code will skip the "preventDefault" if confirmed == true.

  <script type="text/javascript">
  $( function()
  {
      var confirmed = false;
      $('a').click( function( event )
      {
          event.preventDefault();                  
          if ( confirmed == false )
          {
              confirmed = true;
              $(event.target).trigger( 'click' );
          }
      });
  });
  </script>

checking - part 2

Besides that, I'm wondering about the existence of "var confirmed" in general. The code would also work flawless without it since you're not using the confirmed variable in the function anywhere else...

  <script type="text/javascript">
  $( function()
  {
      $('a').click( function( event )
      {
          event.preventDefault();                  
          $(event.target).trigger( 'click' );
      });
  });
  </script>

checking - part 3

Knowing you can replace the trigger with a simple click, did you try that?

 <script type="text/javascript">
  $( function()
  {
      $('a').click( function( event )
      {
          event.preventDefault();                  
          $(event.target).click();
      });
  });
  </script>

checking - part 4

If the click still fails on the target element, it's definitely time to inspect (using "FireBug on Firefox" or alike) if "event.target" holds an object at all. You never know...

checking - part 5

One more thing: I don't see any Document Ready checking, so I hope you've put that script at the end of your file, right before the "</body>" tag. If you load it in the "<head>" without checking Document Ready, it might happen that javascript tries to bind the event to an element that is yet to be loaded into the dom... which would be like throwing feathers against a 9 Inch steel plate... nothing will happen. ;)

that's all

That's all that comes to mind as an answer to your question. One of them could fix the issue you're having. At least, that's what I hope. These little snippets here at stackoverflow leave massive probabilities related to what we don't see. It's hard to be sure what might be missing when you don't have the "complete view". ;)

  • Please click the link to my original question. Most of your suggestions ignore the fact that this code is the simplest proof of a problem I could put together. Point 5 is just wrong. The structure I'm using is on ready. $( function() { /* ... /* } ); is a shortcut to onready. – Tim G Dec 13 '11 at 04:09
0

How about doing something like creating a link offscreen that would handle the final click? That way, you can prevent the click of the original link, handle the code, then proceed to trigger the click of the hidden copy.

Coronus
  • 587
  • 6
  • 26
  • That would cause issues with Dom changes after the plugin initializes. I need to be able to allow bound click handlers to fire on the trigger. – Tim G Dec 15 '11 at 04:33