How to remove drag(Ghost) image? - javascript

How to remove ghost image when drag the image. We have tried code, its working in Firefox and chrome but not working in Safari. Please any one help what is the mistake of my code.
https://jsfiddle.net/rajamsr/Lfuq5qb6/
document.addEventListener("dragstart", function( event ) {
dragged = event.target;
event.dataTransfer.setDragImage(dragged, 11111110, 10);
}, false);

You can prevent showing the ghost preview by showing empty/transparent image:
document.addEventListener("dragstart", function( event ) {
var img = new Image();
img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=';
event.dataTransfer.setDragImage(img, 0, 0);
}, false);

Do not use the event.target as an argument to setDragImage, this is probably causing the memory issues here.
You can always add a custom image, the image could as well be a transparent PNG.
Here is an example how that goes.
var dragMe = document.getElementById("drag-me"),
img = new Image();
img.onload = function () {
dragMe.addEventListener("dragstart", function(e) {
e.dataTransfer.setDragImage(img, 0, 0);
}, false);
}
img.src = "http://placehold.it/150/000000/ffffff?text=GHOST";
#drag-me {
width: 100px;
height: 100px;
background-color: black;
line-height: 100px;
text-align: center;
}
#drag-me > img {
vertical-align: middle;
}
<div id="drag-me">
<img src="https://jsfiddle.net/img/logo.png" draggable="true" />
</div>
Another option would be to just clone the node element and set its visibility to hidden. But for this option to work it is necessary to add the cloned element to the DOM.
An example with the cloned node could look like this. I am not hiding the node completely, so you can see what is happening.
var dragMe = document.getElementById("drag-me");
dragMe.addEventListener("dragstart", function(e) {
var clone = this.cloneNode(true);
clone.style.opacity = 0.1; // use opacity or
//clone.style.visibility = "hidden"; // visibility or
//clone.style.display = "none"; // display rule
document.body.appendChild(clone);
e.dataTransfer.setDragImage(clone, 0, 0);
}, false);
#drag-me {
width: 100px;
height: 100px;
background-color: black;
line-height: 100px;
text-align: center;
}
#drag-me > img {
vertical-align: middle;
}
<div id="drag-me">
<img src="https://jsfiddle.net/img/logo.png" draggable="true" />
</div>

You could just use the event.target and position it using the window's outerWidth and outerHeight
document.addEventListener("dragstart", function( event ) {
event.dataTransfer.setDragImage(event.target, window.outerWidth, window.outerHeight);
}, false);

Your code is causing memory issues.
Instead use css to stop user-drag/select, this works in most browsers but there is a bug it seems in firefox that it doesn't work so add ondragstart="return false;" to img tags to fix this see https://jsfiddle.net/Lfuq5qb6/1/
<img class="normal-logo hidden-xs" src="..." alt="logo" ondragstart="return false;"/>
img{
user-drag: none;
user-select: none;
-moz-user-select: none;
-webkit-user-drag: none;
-webkit-user-select: none;
-ms-user-select: none;
}

function removeGhosting(event) {
if(!(event instanceof MouseEvent)) {
console.info("Parameters must be of type MouseEvent!")
return;
}
let dragIcon = document.createElement('img');
dragIcon.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',
dragIcon.width = 0;
dragIcon.height = 0;
dragIcon.opacity = 0;
if(event.dataTransfer) {
event.dataTransfer.setDragImage(dragIcon,0, 0);
}
}
enter link description here

You can set img draggable attribute to false.
<img id="" src="..." draggable="false">
And to disable selection on images, you can use
<style>
img{
user-drag: none;
user-select: none;
-moz-user-select: none;
-webkit-user-drag: none;
-webkit-user-select: none;
-ms-user-select: none;
}
</style>

Related

How can I enable draggable when mouse is already down on element and already moved?

I have code written to allow an HTML element to be dragged after the mouse has been down over that element for a certain period of time.
The problem is that when I am using native HTML drag and drop, and I enable the draggable property when this timeout is up (the mouse has been down on that element for that period of time), if the mouse had been moved while it was down before that timeout was up, HTML will not trigger a dragstart event or even start dragging the element.
There is an example below.
var t;
function startDelayedDrag() {
clearTimeout(t);
document.getElementById('dragtarget').draggable = false;
console.log('mousedown')
t = setTimeout(function() {
console.log('dragging enabled')
document.getElementById('dragtarget').draggable = true;
}, 1000);
}
.droptarget {
float: left;
width: 100px;
height: 35px;
margin: 15px;
padding: 10px;
border: 1px solid #aaaaaa;
user-select: none;
}
<div class="droptarget">
<p onmousedown="startDelayedDrag()" id="dragtarget">Drag me!</p>
</div>
<div class="droptarget"></div>
This one is tricky and it might be different from what you had in mind, but here is goes an idea how to solve your issue:
Start the drag event
Hide the drag object by setting an image using setDragImage
Clone the drag element node, hide the clone and add it to the document (since it's not possible to change the image set by setDragImage)
Start the timeout to change the visibility of the ghost element
This could be yet improved in many ways, but I think you can get the mechanics of how it works as it is. As a reference see the following snippet:
const [$drag] = document.getElementsByClassName('drag')
const [$pixel] = document.getElementsByClassName('pixel')
let $ghost = null
$drag.addEventListener("dragstart", e => {
// set the current draged element invisible
e.dataTransfer.setDragImage($pixel, 0, 0)
// create a ghost element
$ghost = $drag.cloneNode(true)
$ghost.style.position = "absolute"
$ghost.style.display = "none"
document.body.appendChild($ghost)
setTimeout(() => {
$ghost.style.display = 'block'
}, 1000)
})
$drag.addEventListener("drag", e => {
// keep the ghost position to follow the mouse while dragging
$ghost.style.left = `${e.clientX}px`
$ghost.style.top = `${e.clientY}px`
}, false);
$drag.addEventListener("dragend", e => {
// remove the ghost
if ($ghost.parentNode) $ghost.parentNode.removeChild($ghost)
}, false)
.content {
display: flex;
}
.box {
width: 100px;
height: 35px;
padding: 10px;
margin: 10px;
border: 1px solid #aaaaaa;
}
.drop {
user-select: none;
}
.drag {
text-align: center;
}
.pixel {
width: 1px;
height: 1px;
background-color: white;
}
<div class="content">
<div draggable="true" class="drag box">Drag</div>
<div class="drop box"></div>
<div class="pixel"></div>
</div>

Drawing with touch html canvas only drawing dots

Recently I have been working with responsive design and I decided I would make a page with some quotes and a place where you can then sign your name. The place where you can sign uses mouse and touch events to draw on canvas. It works perfectly with a mouse but with touch when I try to drag and draw it just highlights box but I can draw one dot at a time by tapping but I want it to drag normally like with the mouse. I have set default off for things however I still cannot fix this issue. I don't know if it has something to do with the canvas being highlighted with touch. Here is the individual code for the touch not in my site but in a canvas webpage by itself:
<html>
<head>
<title>Sketchpad</title>
<script type="text/javascript">
var canvas;
var ctx;
var mouseX;
var mouseY;
var mouseDown=0;
var touchX,touchY;
function drawCircle(ctx,x,y,size) {
r=0; g=0; b=0; a=255;//opaque
ctx.fillStyle = "rgba("+r+","+g+","+b+","+(a/255)+")";
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
}
function clearCanvas(canvas,ctx) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
//get mouse position
function getMousePosition(moves) {
if (!moves)
var moves = event;
if (moves.offsetX) {
mouseX = moves.offsetX;
mouseY = moves.offsetY;
}
else if (moves.layerX) {
mouseX = moves.layerX;
mouseY = moves.layerY;
}
}
function canvases_touchStart() {
getTouchPosition();
drawCircle(ctx,touchX,touchY,4);
//prevents another mousedown
event.preventDefault();
}
//draw and prevent default scrolling with touch
function canvase_touchMove(moves) {
getTouchPosition(moves);
drawCircle(ctx,touchX,touchY,4);
//prevent scrolling
event.preventDefault();
}
function getTouchPosition(moves) {
if (!moves)
var moves = event;
if(moves.touches) {
if (moves.touches.length == 1) { //one finger
var touch = moves.touches[0]; //get info for finger
touchX=touch.pageX-touch.target.offsetLeft;
touchY=touch.pageY-touch.target.offsetTop;
}
}
}
function canvase_mouseDown() {
mouseDown=1;
drawCircle(ctx,mouseX,mouseY,4);
}
function canvase_mouseUp() {
mouseDown=0;
}
function canvase_mouseMove(moves) {
getMousePosition(moves);
if (mouseDown==1) {
drawCircle(ctx,mouseX,mouseY,4);
}
}
function init() {
canvas = document.getElementById('canvase');
if (canvas.getContext)
ctx = canvas.getContext('2d');
//check for ctx first
if (ctx) {
canvas.addEventListener('mousedown', canvase_mouseDown, false);
canvas.addEventListener('mousemove', canvase_mouseMove, false);
window.addEventListener('mouseup', canvase_mouseUp, false);
canvas.addEventListener('touchstart', canvase_touchStart, false);
canvas.addEventListener('touchmove', canvase_touchMove, false);
}
}
</script>
<style>
#whole {
/*prevents highlighting text*/
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
#instructions {
width:30%;
height:15%;
padding:2px;
border-radius:4px;
font-size:120%;
display: block;
margin-left: auto;
margin-right: auto;
text-align:center;
}
#board {
display: block;
margin-left: auto;
margin-right: auto;
margin-top: 1%;
}
#canvase {
height:300;
width:400;
border:2px solid black;
border-radius:4px;
position:relative;
display: block;
margin-left: auto;
margin-right: auto;
margin-top: 1%;
}
#clearbutton {
font-size: 150%;
padding: 2px;
-webkit-appearance: none;
background: green;
border: 1px solid black;
color:white;
display: block;
margin-left: auto;
margin-right: auto;
}
</style>
</head>
<body onload="init()">
<div id="whole">
<div id="instructions">
Sign name by tapping or dragging in box (draw slowly to prevent single <br/><br/>
<input type="submit" value="Clear Board" id="clearbutton" onclick="clearCanvas(canvas,ctx);">
</div>
<div id="board">
<canvas id="canvase" height="300" width="400">
</canvas>
</div>
</div>
</body>
</html>
Does anyone have any thoughts on this issue or a way to fix it?
Seems like the canvase_touchStart event is causing the issue.
You may remove the line canvas.addEventListener('touchstart', canvase_touchStart, false); and the corresponding function function canvases_touchStart() {...}
CODEPEN
Hope this helps.
PS: To know how to create a smoother effect, you may refer to this CODEPEN as well.

Prevent ghost image on dragging non-draggable elements?

I'm creating a website that utilizes the HTML5 Drag and Drop API.
However, to increase the user experience, I'd like to prevent ghost images when a user drags non-draggable elements. Is this even possible?
Further, almost every element seems " draggable " by default. One can click and then quickly drag pretty much any element in a browser, which creates a ghost image along with a no-drop cursor. Is there any way to prevent this behaviour?
draggable="false" doesn't work.
user-select: none doesn't work.
pointer-events: none doesn't work.
event.preventDefault() on mousedown doesn't work.
event.preventDefault() on dragstart doesn't work either.
I'm out of ideas and so far it's proven incredibly difficult to find information about this online. I have found the following thread, but, again, draggable="false" doesn't seem to work in my case.
Below is a screenshot that demonstrates it doesn't work; of course you can't see my cursor in the screenshot, but you can see how I've dragged the numbers to the left despite that.
I believe the issue might have something to do with its parent having dragover and drop events associated with it. I'm still dumbfounded nonetheless.
HTML
...
<body>
...
<div id="backgammon-board-container">
<div class="column" id="left-column">
<div class="quadrant" id="third-quadrant">
<div class="point odd top-point" id="point-13-12"><text>13</text>
<div class="checker player-one-checker" id="checker-03" draggable="true"></div>
</div>
</div>
</div>
...
</div>
</body>
</html>
CSS
#backgammon-board-container {
height: 100vh;
width: 60vw;
position: absolute;
right: 0;
display: flex;
}
.column {
height: 100%;
display: flex;
flex-direction: column; /* column-reverse for player two perspective */
}
#left-column {
flex: 6;
}
.quadrant {
flex: 1;
display: flex;
}
.point {
flex: 1;
padding: 10px 0;
display: flex;
flex-direction: column;
align-items: center;
}
.checker {
z-index: 1;
width: 48px;
height: 48px;
border-radius: 50%;
}
text {
position: fixed;
font-family: impact;
font-size: 24px;
color: white;
display: flex;
justify-content: center;
align-items: center;
user-select: none;
pointer-events: none;
}
JS
const p1checkers = document.getElementsByClassName('player-one-checker');
const p2checkers = document.getElementsByClassName('player-two-checker');
const pointClass = document.getElementsByClassName('point');
function setTurn(player) {
if (player === 'p1') {
allowCheckerMovement = p1checkers;
disallowCheckerMovement = p2checkers;
} else {
allowCheckerMovement = p2checkers;
disallowCheckerMovement = p1checkers;
}
// enable checker control for player
for (var i = 0; i < allowCheckerMovement.length; i++) {
allowCheckerMovement[i].style.cursor = 'pointer';
allowCheckerMovement[i].setAttribute('draggable', true);
allowCheckerMovement[i].addEventListener('dragstart', start); // for drag-and-drop.js
allowCheckerMovement[i].addEventListener('dragend', stop); // for drag-and-drop.js
}
// disable checker control for player
for (var i = 0; i < disallowCheckerMovement.length; i++) {
disallowCheckerMovement[i].style.cursor = 'default';
disallowCheckerMovement[i].setAttribute('draggable', false);
disallowCheckerMovement[i].removeEventListener('dragstart', start); // for drag-and-drop.js
disallowCheckerMovement[i].removeEventListener('dragend', stop); // for drag-and-drop.js
}
// allow drag and drop
for (var i = 0; i < pointClass.length; i++) {
pointClass[i].addEventListener('dragover', allowDrop); // for drag-and-drop.js
pointClass[i].addEventListener('drop', droppedOn); // for drag-and-drop.js
}
}
function start(event) {
var checker = event.target;
event.dataTransfer.setData('text/plain', checker.id);
event.dataTransfer.effectAllowed = 'move';
window.requestAnimationFrame(function(){
checker.style.visibility = 'hidden';
});
}
function allowDrop(event) {
event.preventDefault();
}
function droppedOn(event) {
event.preventDefault();
var data = event.dataTransfer.getData('text/plain');
event.target.appendChild(document.getElementById(data));
}
function stop(event){
var element = event.srcElement;
window.requestAnimationFrame(function(){
element.style.visibility = 'visible';
});
}
This is the solution you're looking for. ;)
For anything that DOES need to be draggable, just add the 'enable-drag' CSS class.
$('*:not(".enable-drag")').on('dragstart', function (e) {
e.preventDefault();
return false;
});

Polymer - before/after slider

I'm trying to create a before/after image slider similar to before-after.js or cocoen as a custom Polymer Web Component for Rails. However, I'm having some JavaScript issues with my implementation. Some have already been solved during the course of this question. The main remaining problems are:
Only the first instance of the component on the page works!
The web component's DOM elements are not found by the JS unless
they are inside window.onload, even though the script is included
at the very end of the .html for the component.
Here are the HTML, JS, and CSS files for the slider:
image-slider.html:
<dom-module id="image-slider">
<link rel="stylesheet" href="image-slider.css" />
<template>
<div id="dual-wrapper" style$="border: [[border]];
border-radius: [[border_radius]]; width: [[width]];
height: [[height]]; margin: [[margin]];">
<div id="img-snd-wrap">
<img src$="[[snd]]" class="img-snd">
</div>
<div id="img-fst-wrap">
<img src$="[[fst]]" class="img-fst">
</div>
<div class="img-blind" style="width: [[width]]; height: [[height]]"></div>
<div id="img-transition-slider" style$="height: [[height]];">
<div id="img-transition-slider-handle"
style$="margin-top: calc([[height]]/2 - [[handle_height]]/2);
height: [[handle_height]];">
</div>
</div>
</div>
</template>
<script src="image-slider.js"></script>
</dom-module>
image-slider.js:
Polymer({
is: "image-slider",
properties: {
fst: {
type: String
},
snd: {
type: String
},
width: {
type: String
},
height: {
type: String
},
border: {
type: String,
value: "none"
},
border_radius: {
type: String,
value: "0px"
},
handle_height: {
type: String,
value: "80px"
}
},
attached: function () {
var slider, first, second, container, x, prev_x, containerWidth;
slider = this.$['img-transition-slider'];
console.log(slider);
first = this.$['img-fst-wrap'];
second = this.$['img-snd-wrap'];
container = this.$['dual-wrapper'];
slider.onmousedown = function(e) {
document.body.style.cursor = "col-resize";
containerWidth = container.clientWidth;
prev_x = x - slider.offsetLeft;
slider.querySelector("#img-transition-slider-handle").style["background-color"] = '#888';
};
document.onmousemove = function(e) {
// X coordinate based on page, not viewport.
if (e.pageX) { x = e.pageX; }
// If the object specifically is selected, then move it to
// the X/Y coordinates that are always being tracked.
if(slider) {
var toReposition = (x - prev_x);
var newPosition = ((toReposition > containerWidth) ?
containerWidth - 2
: ((toReposition < 0) ?
0
:
toReposition
));
slider.style["margin-left"] = newPosition + 'px';
second.style["width"] = newPosition + "px";
first.style["width"] = (containerWidth - newPosition) + "px";
first.style["margin-left"] = newPosition + "px";
first.getElementsByTagName("img")[0].style["margin-left"] = (-newPosition) + "px";
}
};
document.onmouseup = function() {
document.body.style.cursor = "default";
slider.querySelector("#img-transition-slider-handle").style["background-color"] = '#555';
slider = false;
};
}
});
image-slider.css:
:host {
display: block;
}
#dual-wrapper {
display: block;
overflow: hidden;
position: relative;
-moz-user-select: -moz-none;
-khtml-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
#img-fst-wrap, #img-snd-wrap {
display: block;
position: absolute;
overflow: hidden;
}
.img-blind {
display: block;
position: absolute;
}
#img-transition-slider {
position: absolute;
display: block;
width: 0px;
border: 1px solid #333;
border-top: none;
border-bottom: none;
}
#img-transition-slider:hover {
cursor: col-resize;
}
#img-transition-slider-handle {
width: 10px;
margin-left: -5px;
background-color: #555;
border-radius: 2px;
-webkit-transition: background-color .3s;
transition: background-color .3s;
}
If the web component is not duplicable, it is because you use document.getElementById() on ids that are themselves duplicated. So only the first (or last) element defined with this id will always be returned.
You should use this.$.[sub-element's id] to select an element inside the subtree of the Polymer element and add mouse event listener from inside the Polymer element, in the attached() callback method:
var slider = this.$['image-transition-slider']
//define the mouse event listeners inside the element
where this is the reference to the custom element itself.

Plain simple select tag styling

I've been looking for a way to style a couple of <select> tags in a madlib-esque fashion.
I want the select box width to be based on what is selected and not show extra whitespace.
I'm looking for a way to make this as cross-browser compatible as possible right now it works fine in webkit but the dreaded arrows show in firefox.
Progressive enhancement JS only, fallback to regular select field.
Here is my fiddle: http://jsfiddle.net/bXJrk/
Any help on achieving this would be greatly appreciated.
$('select').each(function(){
var width = $('option[value='+$(this).val()+']').html();
$('body').append('<span id="'+$(this).val()+'" style="display:none">'+width+'</span>');
var two = $('#'+$(this).val()).width();
$(this).width(two+4).addClass('jsselect');
});
$('select').change(function(){
var width = $('option[value='+$(this).val()+']').html();
$('body').append('<span id="'+$(this).val()+'" style="display:none">'+width+'</span>');
var two = $('#'+$(this).val()).width();
$(this).width(two+4);
});
This is what I got out.
The fiddle: http://jsfiddle.net/wAs7M/4/
Javascript:
$('.replacementcontainer select').each(function(){
var apply = function(el){
var text = $('option[value='+el.val()+']').html();
var span;
if (el.data('initialized'))
{
span = el.parent().next().html(text);
}
else
{
el.data('initialized', true);
el.after('<span id="'+el.val()+'" class="jsselect hiddenspan">'+text+'</span>');
el.wrap($('<span class="selwrapper"></span>'));
span = el.parent().next();
span.addClass('jsselect');
el.addClass('jsselect');
}
el.parent().width(span.width() + 5);
var two = span.width();
};
apply($(this));
$(this).change(function(){ apply($(this)); });
});
CSS:
*{font-family:sans-serif;font-size:15px}
.replacementcontainer {
margin: 10px;
}
.replacementcontainer span {
display: inline-block;
margin-bottom: -4px;
}
.jsselect {
color: #3084CA;
text-decoration: underline;
border: none;
background: none;
padding: 0;
margin: 0;
cursor: pointer;
outline: none;
}
.selwrapper {
display: inline-block;
overflow: hidden;
}
.hiddenspan {
visibility:hidden;
position:absolute;
}

Categories

Resources