Disable permanent active state

This is a known bug in Firefox, see https://bugzilla.mozilla.org/show_bug.cgi?id=193321

The bug has had an on-and-off status with several reports; the behavior is non-standard and browser-specific.

You can build a work-around for it, but you're stuck with javascript. After much searching, I determined that unless you're running in privileged mode (i.e. your code is in an extension), you cannot directly influence the pseudo-selector states. That means you're left with adding/removing a class name:

<a href="#" onmousedown="this.className = '';" ondragend="this.className = 'inactive';" id="foo" >Drag me</a>

Try it: http://jsfiddle.net/frsRS/

If you do have privileged mode, you can use the method that Firebug employs in their CSS Panel, using inIDOMUtils.setContentState

var node = document.getElementById("foo");
var domUtil = Components.classes["@mozilla.org/inspector/dom-utils;1"].getService(Components.interfaces.inIDOMUtils);
domUtil.setContentState( node , 1);

Edit

Here is specific code for binding cross-browser delegates rather than putting the javascript inline (shown here for demonstration purposes, but generally bad practice)

function addEvent(ele, evnt, funct) {
  if (ele.addEventListener) // W3C
    return ele.addEventListener(evnt,funct,false);
  else if (ele.attachEvent)  // IE
    return ele.attachEvent("on"+evnt,funct);
}
addEvent(document.body, 'mousedown', function (e) {        
    if(e.target.tagName == 'A') e.target.style.color = '';

});
addEvent(document.body, 'dragend', function (e) {
    if(e.target.tagName == 'A') e.target.style.color = 'blue';
});

Try it: http://jsfiddle.net/HYJCQ/

This uses the element's style rather than a css class, you can swap out the methods as desired.

Another way, as suggested by Supr, is to remove and immediately re-add the element from DOM. You can accomplish this using a delegate as well:

function addEvent(ele, evnt, funct) {
  if (ele.addEventListener) // W3C
    return ele.addEventListener(evnt,funct,false);
  else if (ele.attachEvent)  // IE
    return ele.attachEvent("on"+evnt,funct);
}
addEvent(document.body, 'dragend', function (e) {
    if(e.target.tagName != 'A') return;
    var parent = e.target.parentNode;
    var sib = e.target.nextSibling;
    parent.insertBefore(
        parent.removeChild(e.target),
        sib
    );
});

Try it: http://jsfiddle.net/ymPfH/

Both methods that utilize delegation are better approaches than looping elements -- that way, the script will apply to any a tags added to the page after load (similar to how jQuery's live or on methods work).

Documentation

  • Bugzilla entry - https://bugzilla.mozilla.org/show_bug.cgi?id=193321
  • Drag and Drop on MDN (ondragend) - https://developer.mozilla.org/en-US/docs/Drag_and_Drop
  • inIDOMUtils - https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/inIDOMUtils

Detach and reattach the link from the DOM tree to disable its active state. Do this when the drag ends and you've got this:

$('a').on('dragend',function(){
    var $this   = $(this),
        $parent = $this.parent(),
        $next   = $(this.nextSibling); // $.next() doesn't include text
    $this.detach().insertBefore($next);     
});

No need to mess with your HTML or CSS or do away with :active. Seems to work in both FF and IE.


Edit: I don't usually write pure Javascript for DOM-handling so the quality of this might not be top notch, but here it is without jQuery:

(function(){
    var previousOnload = window.onload || function noop(){};

    window.onload = function (){

        // Call any previously added onload callbacks
        previousOnload();

        // Add deactivator to each <a> element
        var elements = document.getElementsByTagName('a');
        for (var i=0; i<elements.length; i++){
            elements[i].ondragend = deactivate;
        }

        function deactivate(){
            var parent   = this.parentNode,
                position = this.nextSibling;
            parent.removeChild(this);
            // Using insertBefore instead of appendChild so that it is put at the right position among the siblings
            parent.insertBefore(this, position);
        }
    };

})();

I took care of a few issues that came to mind to make it fully plug-and-play. Tested in Opera, Chrome, Firefox and Internet Explorer.


Edit 2: Inspired by Chris, another way to apply the fix is to use the ondragend attribute directly to connect the deactivator (not tested):

<head>
    <script>
        function deactivate(){
            var parent   = this.parentNode,
                position = this.nextSibling;
            parent.removeChild(this);
            // Using insertBefore instead of appendChild so that it is put at the right position among the siblings
            parent.insertBefore(this, position);
        }
    </script>
</head>
<body>
    <a href="#" ondragend="deactivate()">Drag me</a>
<body>

The downside is that it requires the ondragend attribute with javascript to be specified on each link manually/explicitly. I guess it's a matter of preference.


Final (?) Edit: See comments for delegate/live versions of these and Chris' answer.

Tags:

Javascript

Css