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.