views:

2472

answers:

6

Thanks for the three excellent answers which all identified my problem of using "onclick = ..." instead of "observe( "click",..."

But the award for Accepted Answer has to go to Paolo Bergantino for the mechanism of adding a class name to mark the dragged element, which saved me some more work!


In my HTML I have a table with an image link on each row.

<table class="search_results">
  <tr>
     <td class="thumbnail"><a href="..."><img src="..." /></a></td>
...

An included javascript file contains the code to make the images draggable

$$( ".thumbnail a img" ).each(
  function( img )
  {
    new Draggable( img, {revert:true} );
  }
);

and a simple handler to detect the end of the drag

Draggables.addObserver({
  onEnd:function( eventName, draggable, event )
  {
    // alert ( eventName ); // leaving this in stops IE from following the link
    event.preventDefault(); // Does Not Work !!!
    // event.stop(); // Does Not Work either !!!
  }
});

My idea is that when the image is clicked the link should be followed but when it is dragged something else should happen.

In fact what happens is that when the image is dragged the handler is called but the link is still followed.

I guess that I'm cancelling the wrong event.

How can I prevent the link from being followed after the element is dragged?


edit: added event.stop after trying greystate's suggestion


I now have a basic solution that works for FireFox, Apache, etc. See my own answer below.

But I am still looking for a solution for IE7 (and hopefully IE6).

Another problem when dragging images in IE is that the image becomes detached from the mouse pointer when the tool tip appears, you have to release the mouse and click again on the image to re-acquire the drag. So I'm also looking for any ideas that might help resolve that problem.

A: 

After some experiments I've come up with the following fix.

But I'm less than happy with it as it seems a bit messy and it doesn't work in IE7.

edit

also tried "ev.stop" and "return false" as alternatives to ev.preventDefault. All give the same result, working in every browser except IE.

un-commenting the alert proves that the preventDefault is being executed even in IE

changed onEnd handler to onDrag handler to avoid potential problems with call order of handlers.

$$( ".thumbnail a img" ).each(
  function( img )
  {
    new Draggable( img, {revert:true} );
  }
);

var drag_occurred = false;
$$( ".thumbnail a" ).each(
  function( tag )
  {
    tag.onclick = function( ev )
    {
      if ( drag_occurred )
      {
        // alert( "!" );
        ev.preventDefault();
      }
      drag_occurred = false;
    }
  }
);

Draggables.addObserver({
  onDrag:function( eventName, draggable, event )
  {
    drag_occurred = true;
  }
});
Noel Walters
A: 

I can not test this right now, but as I see it, you're making the <img> draggable, yet you're observing clicks on the <a> element, so the problem is probably that the click event bubbles up to the link, which executes its default action.

Prototype has a stop() method for events that besides preventing the default action, also cancels event bubbling - you could try that in the onEnd function instead of preventDefault().

greystate
Thanks for the suggestion. I tried using Event.stop in both versions but the results are the same. The second version works in most browsers but neither version works in IE.
Noel Walters
A: 

Try doing this

$$( ".thumbnail a" ).each(
  function( tag )
  {
    tag.onclick = function( ev )
    {
      if ( drag_occurred )
      {
        ev.cancelBubble = true;
        if (ev.stopPropagation) ev.stopPropagation();
      }
      drag_occurred = false;
    }
  }
);
Nope, that doesn't work in IE7 or Firefox (didn't test on other browsers)
Noel Walters
+1  A: 

Ok, the way I found is with a "dragging status" variable, similar to your previous post, but it works under both IE7 and FF.

Using Prototype, you can add an observer function for the click event of the html element you want to drag, telling in there event.stop();. This action extends (i.e. Prototype-ize) the event object to be passed to the handler function, thus enriching it of all Prototype add-on methods and making it able to invoke the stop() method without worrying about the browser it's running into, as Prototype manages this aspect for you.

Here's the code of my example:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
      <script type="text/javascript" src="lib/prototype.js"></script>
      <script type="text/javascript" src="src/scriptaculous.js"></script>
    <title>Draggables Test</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <div id="dragme"><a href="http://www.google.it"&gt;Ok, you can drag this one.</a></div>
    <script type="text/javascript">
        function processIt(event) {
            if ( isDragging ) {
                event.stop();
            }
            isDragging = false;
        }

        new Draggable('dragme', { revert: false });
        var isDragging = false;

        $('dragme').observe('click', processIt );
        Draggables.addObserver({
            onDrag:function( eventName, draggable, event ) {
                isDragging = true;
            }
        });
        </script>
  </body>
</html>

The difference is that in your original example you were treating the event as a standard DOM event (see parameters specifications here, bottom of the page), which is ok with FF, but not at all with IE, as it deals with events in a slightly (but definitely) different way. On top of that, the event you were dealing with into your onEnd: handler was't a click event, but a onend event.

Scarlet
+1 from me, Useful answer! I can't see why this got voted back down.
Noel Walters
Now it's back at 1 again - I'm confused
Noel Walters
Thank you anyway, Noel. I appreciated it.
Scarlet
+2  A: 
// for keeping track of the dragged anchor
var anchorID = null;

// register a click handler on all anchors
$$('.thumbnail a').invoke('observe', 'click', function(e) {
    var a = e.findElement('a');
    // stop the event from propagating if this anchor was dragged
    if (a.id == anchorID) {
     e.stop();
     anchorID = null;
    }
});

$$('.thumbnail a img').each(function(img) {
    new Draggable(img, { revert:true });
});

Draggables.addObserver({
    onStart: function(eventName, draggable, e) {
     // store the dragged anchor
     anchorID = e.findElement('a').id;
    }
});
ob
+1 Answers the question, I can't see why this got voted back down either
Noel Walters
Now it's back at 1 again - I'm confused
Noel Walters
+1  A: 
<script>
document.observe("dom:loaded", function() {
    $$( ".thumbnail a img" ).each(function(img) {
        new Draggable(img, {
            revert: true,
            onEnd: function(draggable, event) {
                $(draggable.element).up('a').addClassName('been_dragged');
            }
        });
    });

    $$(".thumbnail a").each(function(a) {
        Event.observe(a, 'click', function(event) {
            var a = Event.findElement(event, 'a');
            if(a.hasClassName('been_dragged')) {
                event.preventDefault();
                // or do whatever else
            }
        });
    });
});
</script>

Works for me on Firefox, IE. It kind of uses your 'marker' idea but I think marking an already dragged element with a class is more elegant than javascript variables.

Paolo Bergantino
Thanks very much - great answer!
Noel Walters