72

When I drag and drop an element on my page the element becomes "ghosted". Basically it gets some transparency value.

Is there some way to make it opacity: 1;?

7
  • 4
    Please give us more to work with. Show us your Javascript to handle the drag & drop event, etc. Mar 15, 2012 at 0:49
  • 2
    Duplicate question? stackoverflow.com/questions/7206622/…
    – Diego
    Mar 15, 2012 at 0:58
  • 24
    Do you really need the code? It's just the native drag and drop in HTML5, can you make it non-transparent? No, it's not a duplicate of that question. Mar 15, 2012 at 8:14
  • 4
    Sadly this question got no answer. I'm struggling with this as well. May 5, 2012 at 22:11
  • 1
    The only way would be to reprogram the drag function! Jun 7, 2012 at 10:55

15 Answers 15

60
+25

It looks like it can't be done. The dragged element is put into container that has it's own, lower than 1, opacity. This means that while you can lower the opacity of the dragged element you can't get it higher than the opacity of the encapsulating element.

It might be possible to override default browser settings for such element, but since nothing is added to DOM during the drag it would be very tricky at best.

6
  • 57
    Is it just me or does this also make other people sad? I mean the idea of dragging and dropping with html5 is awesome right? But I have been trying it for some hours now but it really disappoints me. This opacity issue is one of the reasons..... back to jquery ui.
    – driechel
    Dec 20, 2012 at 14:28
  • 39
    @driechel it makes me so sad to hear "back to jquery".
    – Greg
    May 19, 2014 at 14:38
  • 4
    it can be done use event.target.style.opacity=1; inside drag($event){ .. }
    – Vinayk93
    May 2, 2017 at 8:49
  • 3
    so strange. console.log(window.getComputedStyle(event.target).opacity) and console.log(window.getComputedStyle(event.srcElement).opacity) both return 1, yet it's still translucent. the op must be right. it's gotta be some invisible container from which it inherits the reduced opacity.
    – oldboy
    May 13, 2017 at 6:09
  • 4
    event.target.style.xxx on the drag(event) start handler applies styles to the element selected for drag but seemingly after the ghost copy for dragging is made, so that suggestion for opacity doesn't work (at least on Chrome) and explains the comments above Jul 12, 2017 at 23:02
12

As others have suggested, you will need some sort of mechanism that will:

  1. Hide the element that is being dragged.
  2. Make a clone of the element that is being dragged.
  3. Put the clone in place of the element that is being dragged.
  4. Listen to the drag event to position the clone element.

In practise, looks like this:

function Drag (subject) {
    var dative = this,
        handle,
        dragClickOffsetX,
        dragClickOffsetY,
        lastDragX,
        lastDragY;

    subject.draggable = true;

    dative.styleHandle(subject);

    subject.addEventListener('dragstart', function (e) {    
        handle = dative.makeHandle(subject);

        dragClickOffsetX = e.layerX;
        dragClickOffsetY = e.layerY;

        this.style.opacity = 0;
    });

    subject.addEventListener('drag', function (e) {
        var useX = e.x,
            useY = e.y;

        // Odd glitch
        if (useX === 0 && useY === 0) {
            useX = lastDragX;
            useY = lastDragY;
        }

        if (useX === lastDragX && useY === lastDragY) {
            return;
        }

        dative.translate(useX - dragClickOffsetX, useY - dragClickOffsetY, handle, subject);

        lastDragX = useX;
        lastDragY = useY;
    });

    subject.addEventListener('dragend', function (e) {
        this.style.opacity = 1;

        handle.parentNode.removeChild(handle);
    });
};

/**
 * Prevent the text contents of the handle element from being selected.
 */
Drag.prototype.styleHandle = function (node) {
    node.style['userSelect'] = 'none';
};

/**
 * @param {HTMLElement} subject
 * @return {HTMLElement}
 */
Drag.prototype.makeHandle = function (subject) {
    return this.makeClone(subject);
};

/**
 * Clone node.
 * 
 * @param {HTMLElement} node
 * @return {HTMLElement}
 */
Drag.prototype.makeClone = function (node) {
    var clone;

    clone = node.cloneNode(true);

    this.styleClone(clone, node.offsetWidth, node.offsetHeight);

    node.parentNode.insertBefore(clone, node);

    return clone;
};

/**
 * Make clone width and height static.
 * Take clone out of the element flow.
 *
 * @param {HTMLElement} node
 * @param {Number} width
 * @param {Nubmer} height
 */
Drag.prototype.styleClone = function (node, width, height) {
    node.style.position = 'fixed';
    node.style.zIndex = 9999;
    node.style.width = width + 'px';
    node.style.height = height + 'px';
    node.style.left = '-9999px';

    node.style.margin = 0;
    node.style.padding = 0;
};

/**
 * Used to position the handle element.
 * 
 * @param {Number} x
 * @param {Number} y
 * @param {HTMLElement} handle
 * @parma {HTMLElement} subject
 */
Drag.prototype.translate = function (x, y, handle, subject) {
    handle.style.left = x + 'px';
    handle.style.top = y + 'px';
};

Start with attaching an element:

new Drag(document.querySelector('.element'));

And you have a working drag and drop with full control over the looks of the draggable element. In the above example, I clone the original element to use it as the handle. You can extend the Drag function to customise the handle (e.g. use image to represent the draggable element).

Before you get too excited, there are couple of things to consider:

Update:

I have written a library for touch enabled implementation of WHATWG drag and drop mechanism, https://github.com/gajus/pan.

0
5

As of 2021, You need not clone the element. The browser creates a semi-transparent clone that follow the mouse pointer on drag event. Meanwhile the original dragged item remains intact without any changes.

If you wish to show the original element being dragged to follow the mouse pointer then here's the trick.

Setup:

<!- HTML Code Sample ->
<div draggable="true" 
    ondragstart="onDragStart(event)" ondrag="onDrag(event)"
                    ondragend="onDragEnd(event)">

<!- JS Code Sample ->
var dx = 0, dy = 0, draggedItem = undefined;
function onDragStart(e) {}
function onDrag(e) {}
function onDragEnd(e) {}

Step 1: Save the difference between dragged item's (x, y) co-ordinates and mouse pointer co-ordinates as delta.

function onDragStart(e) {
    draggedItem = e.target;
    dx = e.clientX - draggedItem.getBoundingClientRect().x;
    dy = e.clientY - draggedItem.getBoundingClientRect().y;
    draggedItem.style.position = 'absolute';
}

Step 2: Update the dragged item's position with that of mouse pointer's (x, y) co-ordinates. Remember to subtract the delta.

function onDrag(e) {
    draggedItem.style.left = e.ClientX - dx;
    draggedItem.style.top = e.ClientY - dy;
}

Step 3: Depending upon whether to retain new position for the dragged item or run some custom logic.

function onDragEnd(e) {

    if('retain new position ???'){
        draggedItem.style.left = e.ClientX - dx;
        draggedItem.style.top = e.ClientY - dy;
    } else {
        draggedItem.style.position = 'static';

        // Add your custom logic to handle drag completion
    }

    // Clear temporary data
    draggedItem = undefined;
    dx = dy = 0;
}

This will make the original dragged item to follow the semi-transparent dragged clone which overcomes the effect of opacity. onDrag event is fired every 350ms and creates a decent visualization.

2
  • Are the double dots ".." valid syntax at draggedItem..getBoundingClientRect() ?
    – David Mays
    Jan 16 at 17:43
  • Hi @DavidMays, It should be a single dot. It's strange I never noticed this typo before. I've made the correction. Thank you. Jan 17 at 13:56
3

If you are not dragging and dropping elements from outside of the web page (from the operating system) then you could solve this problem easily by implementing your own drag and drop. There are numerous examples of pure javascript drag and drop which will function perfectly in an HTML5 environment and would be completely customizable by you.

answer: (use the old way)

2

Please see this working fiddle

I have a solution for making an opaque image in the place of ghost and it works fine in chrome.But its not working in FF.I need some body to help me to make it work in Firefox and other browsers. steps 1.We will make our own ghost image and will set it as the drag image.

document.addEventListener("dragstart", function(e) {
var img = document.createElement("img");
img.src = "img/hackergotchi-simpler.png";
e.dataTransfer.setDragImage(img, 5000, 5000);//5000 will be out of the window
}, false);

2.We will clone the image and append it to DOM ondrag

var crt,dragX,dragY;
function drag(ev) {
    crt = ev.target.cloneNode(true);
    crt.style.position = "absolute"; 
    document.body.appendChild(crt);
    ev.dataTransfer.setData("text", ev.target.id);
}

3.Then we will make the clone to move with cursor

    document.addEventListener("dragover", function(ev){
ev = ev || window.event;
dragX = ev.pageX; dragY = ev.pageY;
crt.style.left=dragX+"px";crt.style.top=  dragY+"px";
console.log("X: "+dragX+" Y: "+dragY);
}, false);

4.At Last we will make the clone visibility gone

   document.addEventListener("dragend", function( event ) {crt.style.display='none';});
1

as already mentioned, this is handled by the browser -> you cannot change this behavior, but if you really need this effect try to look for mouse move when mouse is down (short: dragging), check what element is selected and create a copy on the fly that follows your mouse cursor. looks like an ideal job for jQuery ;)

but if you don't need it desperately I wouldn't try to change the browsers' default values, as people are used to it and may be confused if something behaves in another (unknown) way

1

For those (like me) disappointed by the drag&drop html5 implementation, here is a basic, vanilla, cross-browser, pitfalls-free and fully customizable solution:

html:

<div class="dropzone"></div>
<div class="dropzone"></div>
<div class="draggable"></div>

js:

var currentElement, currentDropzone, offsetX, offsetY;
function findZoneUnderPoint(x, y) {
  var dropzones = preview.querySelectorAll(".dropzone");
  for (var i = 0; i < dropzones.length; i++) {
    var box = dropzones[i].getBoundingClientRect();
    if (x > box.left && x < box.right && y > box.top && y < box.bottom) {
      return dropzones[i];
    }
  }
}
function onMouseDown(event) {
  currentElement = event.target.closest(".draggable");
  if (currentElement) {
    var box = currentElement.getBoundingClientRect();
    offsetX = event.clientX - box.x;
    offsetY = event.clientY - box.y;
    currentElement.classList.add("drag");           
    currentElement.style.width = box.width.toFixed()+"px";
    currentElement.style.height = box.height.toFixed()+"px";    
    currentElement.style.left = (event.clientX - offsetX) + "px";
    currentElement.style.top = (event.clientY - offsetY) + "px";
    currentElement.style.position = "fixed";
    currentElement.style.zIndex = "999";
    this.addEventListener("mousemove", onMouseMove);
    this.addEventListener("mouseup", onMouseUp);
  }
}
function onMouseMove(event) {
  currentElement.style.left = (event.clientX - offsetX) + "px";
  currentElement.style.top = (event.clientY - offsetY) + "px";
  var dropzone = findZoneUnderPoint(event.clientX, event.clientY);
  if (dropzone !== currentDropzone) {
    if (dropzone) {
      // -> drag enter zone
    }
    if (currentDropzone) {
      // -> drag leave zone
    }
    currentDropzone = dropzone;
  }
}
function onMouseUp(event) {
  var dropzone = findZoneUnderPoint(event.clientX, event.clientY);
  if (dropzone) {
    // -> drag complete
  } else {
    // -> drag canceled
  }
  currentElement = null;
  document.removeEventListener("mouseup", onMouseUp);
  document.removeEventListener("mousemove", onMouseMove);
}
document.addEventListener("mousedown", onMouseDown);

Note: Element.closest() polyfill is needed for ie support.

1

Struggled with this for a while then found this, https://laracasts.com/discuss/channels/vue/drag-and-drop-without-low-opacity

worked first time. Changed it very slightly to work with React, this is all you need to do:

const onDragStart = (ev) => {
    var hideDragImage = ev.target.cloneNode(true);
    hideDragImage.id = "hideDragImage-hide"

    var dragImage = ev.target.cloneNode(true);
    dragImage.id = "draggeimage";
    dragImage.style.position = "absolute";

    hideDragImage.style.opacity = 0;
    document.body.appendChild(hideDragImage);
    document.body.appendChild(dragImage);
    ev.dataTransfer.setDragImage(hideDragImage, 0, 0);
}

const onDragEnd = (ev: DragEvent<HTMLDivElement>) => {
    var hideDragImage = document.getElementById('hideDragImage-hide');
    var dragImage = document.getElementById('draggeimage');
    hideDragImage.remove();
    dragImage.remove();
}

const onDrag = (ev: DragEvent<HTMLDivElement>) => {
    var dragImage = document.getElementById('draggeimage');
    if(dragImage) {
        dragImage.style.left = ev.pageX + "px";
        dragImage.style.top = ev.pageY + "px";
    }
}
0

altough this is propably not the real solution of the core problem, I have an idea what might work, at least I tested this in one of my GWT project some time ago and it worked, so I guess it might work in native JS as well altough I have no code example:

  1. Instead of using the dragging function, create a new element which equals the element that should be dragget at the original position of the element to be dragged. The original Element to be dragged should be made invisible now. Implement a logic that restores the orignal element and removes the clone as soon as the mouse isn't pressed anymore.

  2. Make the clone Element stick to the mouse's position. The event that removes the clone has also to check if the mouse is released while the element to drag is positioned over an area where the original ement could be dragged to.

  3. If true, remove the clone and move the original element to the target container.

There is definitely a lot of adjustment work to do in CSS and JS as well, but It might be a trick to overwhelm the JS standard.

1
  • that's what I proposed to do, but I'm not sure if he is still interested in a solution after reading c2h5oh's answer ;)
    – Peter
    Jun 11, 2012 at 13:21
0

I think the transparency doesn't come from the web contents but from the browser and OS. If you want to change the opacity, you must to tweak or hack the browser and OS

0

A working example out of 2012 can be viewed on chrome (you have to use chrome) under "chrome://apps/". If you'd like to know exactly how they did it open the dev tools (strg + shift + i since left click is used for a custom context menu) and start reverse engineering (line 7241 in index.html is a good starting point).

Here is a quick summery:

They cloned the dragged element on dragstart, added it into some top-level container and positioned it to the cursor on drag. In order to avoid blocking the events to the actual elements, they styled the clone with pointer-events: none;

0

hide original image using event.dataTransfer.setDragImage(new Image(), 0, 0); on dragstart. onDrag get the ghost image , update position using event.pageX, event.pageY

-2

Please don't ask me why this works, but if you set the opacity to 0.99999 instead of 1 it works for me. I'm really curious to why so if anyone knows, please post your thoughts.

1
  • Tried this just now with Firefox, doesn't work. I set the opacity of the item being dragged to 0.999 both before and on the dragstart event. Ghost effect is still there.
    – David Tran
    Mar 23, 2021 at 3:58
-4

If you are using JavaScript then in the function which handles the dragStart event include set the style opacity to 1 example:

function dragStartHandler(e) {
   this.style.opacity = '1.0';
}
1
  • 1
    No difference... this refers to the target element, which is a span element in my case. Which is why I don't see that this would work. Mar 15, 2012 at 8:13
-7

Suggestion, do the following, now for this I m using jQuery, try it, before saying something wont work, we are not here to give answers, but here to point you in the right direction.

function dragStartHandler(e) { 
   $("span.{CLASS}")addClass('overdrag'); 
} 

then you need to come up with away to tell that it has stoped draging and dropped into position, and then to RemoveClass('overdrag');

This is not hard to do, so I think you should be able to do it. I want to thank @DonaldHuckle as this is really his solution not mine.

2
  • 12
    This is a Q and A we are here to give answers Jun 12, 2012 at 14:06
  • yes but no answers solved this issue, so I used the best parts of the answers and came up with this code that worked @HotHeadMartin Jul 7, 2017 at 5:08

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.