How to change the cursor icon when dragging in HTML5? - javascript

I need to set the icon for cursor when a user is dragging DIV (red div in the following example).
I have tried several attempt, including using CSS cursor:move and event.dataTransfer.dropEffect with no success, as the icon always show up a "crossed circle".
Any ideas how to solve this issue using HTML5 drag-and-drop API?
http://jsbin.com/hifidunuqa/1/
<script>
window.app = {
config: {
canDrag: false,
cursorOffsetX: null,
cursorOffsetY: null
},
reset: function () {
this.config.cursorOffsetX = null;
this.config.cursorOffsetY = null;
},
start: function () {
document.getElementById('target').addEventListener('dragstart', function (event) {
console.log('+++++++++++++ dragstart')
this.config.cursorOffsetX = event.offsetX;
this.config.cursorOffsetY = event.offsetY;
this.adjustPostion(event);
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.dropEffect = 'move';
}.bind(this));
document.getElementById('target').addEventListener('drag', function (event) {
console.log('+++++++++++++ drag')
this.adjustPostion(event);
}.bind(this));
document.getElementById('target').addEventListener('dragend', function (event) {
console.log('+++++++++++++ dragend')
this.reset();
}.bind(this));;
},
adjustPostion: function (event) {
if (event.pageX <= 0 || event.pageY <= 0) {
console.log('skipped');
return;
}
var elm = document.getElementById('target');
elm.style.left = (event.pageX - this.config.cursorOffsetX) + 'px';
elm.style.top = (event.pageY - this.config.cursorOffsetY) + 'px';
console.log(event.pageX);
console.log(event.pageY);
}
};
</script>

use mousedown and mousemove
window.app = {
dragging: false,
config: {
canDrag: false,
cursorOffsetX: null,
cursorOffsetY: null
},
reset: function () {
this.config.cursorOffsetX = null;
this.config.cursorOffsetY = null;
},
start: function () {
document.getElementById('target').addEventListener('mousedown', function (event) {
console.log('+++++++++++++ dragstart');
this.dragging = true;
this.config.cursorOffsetX = event.offsetX;
this.config.cursorOffsetY = event.offsetY;
this.adjustPostion(event);
}.bind(this));
document.getElementById('target').addEventListener('mousemove', function (event) {
if (this.dragging) {
console.log('+++++++++++++ drag');
event.target.style.cursor = 'move';
this.adjustPostion(event);
}
}.bind(this));
document.getElementById('target').addEventListener('mouseup', function (event) {
console.log('+++++++++++++ dragend');
this.dragging = false;
event.target.style.cursor = 'pointer';
this.reset();
}.bind(this));
},
adjustPostion: function (event) {
if (event.clientX <= 0 || event.clientY <= 0) {
console.log('skipped');
return;
}
var elm = document.getElementById('target');
elm.style.left = (event.clientX - this.config.cursorOffsetX) + 'px';
elm.style.top = (event.clientY - this.config.cursorOffsetY) + 'px';
console.log(event.pageX);
console.log(event.pageY);
}
};
#target {
position: absolute;
top: 100px;
left: 100px;
width: 400px;
height: 400px;
background-color: red;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
user-select: none;
}
#ui1 {
position: absolute;
top: 50px;
left: 50px;
width: 100px;
height: 400px;
background-color: blue;
z-index: 100;
}
#ui2 {
position: absolute;
top: 50px;
left: 550px;
width: 100px;
height: 400px;
background-color: green;
z-index: 100;
}
<!-- simulate -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>title</title>
</head>
<body onload="window.app.start();">
<div id="ui1"></div>
<div id="ui2"></div>
<div id="target"></div>
</body>
</html>

Do you actually need the Drag API? I found that I was using the Drag API because I was having trouble with the reliability of mouse events (mouseups not being captured, for example).
The Drag API is only for drag-and-drop functionality, and, if you're simply fiddling with the reliability of your clicking and pointing events, a new API, .setPointerCapture is made to handle these cases more effectively. Here's the minimal viable way to achieve this:
el.onpointerdown = ev => {
el.onpointermove = pointerMove
el.setPointerCapture(ev.pointerId)
}
pointerMove = ev => {
console.log('Dragged!')
}
el.onpointerUp = ev => {
el.onpointermove = null
el.releasePointerCapture(ev.pointerId)
}
Beautifully, you will maintain full control over your cursor's display style.

I didn't care about a particular cursor, I just wanted to get rid of the "crossed circle" one. My solution was to include dragover event (with following function) to all elements, that already had dragenter, dragstart and dragend events.
function dragover(event)
{
event.dataTransfer.dropEffect = "move";
event.preventDefault();
}

Adding event.dataTransfer.setData(); should solve the problem. Once the element is recognized as draggable the browser will add a move cursor automatically once you drag. Of course, you will have to remove all other cursor: move declarations to see the cursor changing while dragging.
Minimal example:
document.getElementById('target').addEventListener('dragstart', function (event) {
event.dataTransfer.setData( 'text/plain', '' );
}.bind(this));
If you still want to change the icon (e.g. to use a custom drag icon), you could access the element style using event.target.style.cursor.
For more info see MDN Drag & Drop and MDN Recommended Drag Types

Related

'pointerup' event not firing on desktop, works fine on mobile

I'm trying to come up with a cross-device code that handles pointer events.
I'm running this code successfully on Chrome/Android (using USB debugging), but the Chrome desktop just acts up and keeps firing pointermove after the mouse has been released.
(Another problem is that the moves are not as smooth as on the mobile)
Playable demo
These SO posts don't solve my problem:
event-listener-with-pointerup-not-firing-when-activated-from-touchscreen
pointerup-event-does-not-fire-for-mouse-actions-on-a-link-when-pointermove-has-b
The "pointerup" Event is assigned to the #canvas, but such event will never occur because the mouse is actually above the generated DIV circle.
Since your circles are just visual helpers, set in CSS
.dot {
/* ... */
pointer-events: none;
}
Also, make sure to use Event.preventDefault() on "pointerdown".
Regarding the other strategies for a seamless experience, both on desktop and on mobile (touch):
assign only the "pointerdown" Event to a desired Element (canvas in your case)
use the window object for all the other events
Edited example:
const canvas = document.getElementById('canvas');
function startTouch(ev) {
ev.preventDefault();
const dot = document.createElement('div');
dot.classList.add('dot');
dot.id = ev.pointerId;
dot.style.left = `${ev.pageX}px`;
dot.style.top = `${ev.pageY}px`;
document.body.append(dot);
}
function moveTouch(ev) {
const dot = document.getElementById(ev.pointerId);
if (!dot) return;
dot.style.left = `${ev.pageX}px`;
dot.style.top = `${ev.pageY}px`;
}
function endTouch(ev) {
const dot = document.getElementById(ev.pointerId);
if (!dot) return;
removeDot(dot);
}
function removeDot(dot) {
dot.remove();
}
canvas.addEventListener('pointerdown', startTouch);
addEventListener('pointermove', moveTouch);
addEventListener('pointerup', endTouch);
addEventListener('pointercancel', endTouch);
.dot {
background-color: deeppink;
width: 2rem;
height: 2rem;
border-radius: 50%;
transform: translate(-50%, -50%);
position: absolute;
pointer-events: none; /* ADD THIS! */
}
#canvas {
height: 50vh;
background-color: black;
touch-action: none;
}
body {
margin: 0;
}
<div id="canvas"></div>
The code needs also this improvement:
Don't query the DOM inside a pointermove event
Using CSS vars
As per the comments section here's a viable solution that uses custom properties CSS variables and JS's CSSStyleDeclaration.setProperty() method.
Basically the --x and --y CSS properties values are updated from the pointerdown/move event handlers to reflect the current clientX and clientY values:
const el = (sel, par) => (par || document).querySelector(sel);
const elNew = (tag, prop) => Object.assign(document.createElement(tag), prop);
const canvas = document.getElementById('canvas');
const pointersDots = (parent) => {
const elParent = typeof parent === "string" ? el(parent) : parent;
const dots = new Map();
const moveDot = (elDot, {clientX: x,clientY: y}) => {
elDot.style.setProperty("--x", x);
elDot.style.setProperty("--y", y);
};
const onDown = (ev) => {
ev.preventDefault();
const elDot = elNew("div", { className: "dot" });
moveDot(elDot, ev);
elParent.append(elDot);
dots.set(ev.pointerId, elDot);
};
const onMove = (ev) => {
if (dots.size === 0) return;
const elDot = dots.get(ev.pointerId);
moveDot(elDot, ev);
};
const onUp = (ev) => {
if (dots.size === 0) return;
const elDot = dots.get(ev.pointerId);
elDot.remove();
dots.delete(ev.pointerId);
};
canvas.addEventListener('pointerdown', onDown);
addEventListener('pointermove', onMove);
addEventListener('pointerup', onUp);
addEventListener('pointercancel', onUp);
};
// Init: Pointers helpers
pointersDots("#canvas");
* {
margin: 0;
}
.dot {
--x: 0;
--y: 0;
pointer-events: none;
position: absolute;
width: 2rem;
height: 2rem;
border-radius: 50%;
background-color: deeppink;
transform: translate(-50%, -50%);
left: calc(var(--x) * 1px);
top: calc(var(--y) * 1px);
}
#canvas {
margin: 10vh;
height: 80vh;
background-color: black;
touch-action: none;
}
<div id="canvas"></div>

How do I see if one element is touching another in JavaScript without clicking anything

OK so I've tried one thing from a different question and it worked, but not the way I wanted it to. it didn't work the way I wanted it to! You literally had to click when two objects were touching so it would alert you, if somebody can figure out a way to detect if two elements are touching without having to click that would be a life saver! So I hope you people who read this request please respond if you know how. this is the code below. so one object is moving and i want it to make it stop when the object hits the player (i am making a game) the movement is by px.... i want it to keep testing if one object hits the player, and if it does i want it to stop everything.
var boxes = document.querySelectorAll('.box');
boxes.forEach(function (el) {
if (el.addEventListener) {
el.addEventListener('click', clickHandler);
} else {
el.attachEvent('onclick', clickHandler);
}
})
var detectOverlap = (function () {
function getPositions(elem) {
var pos = elem.getBoundingClientRect();
return [[pos.left, pos.right], [pos.top, pos.bottom]];
}
function comparePositions(p1, p2) {
var r1, r2;
if (p1[0] < p2[0]) {
r1 = p1;
r2 = p2;
} else {
r1 = p2;
r2 = p1;
}
return r1[1] > r2[0] || r1[0] === r2[0];
}
return function (a, b) {
var pos1 = getPositions(a),
pos2 = getPositions(b);
return comparePositions(pos1[0], pos2[0]) && comparePositions(pos1[1], pos2[1]);
};
})();
function clickHandler(e) {
var elem = e.target,
elems = document.querySelectorAll('.box'),
elemList = Array.prototype.slice.call(elems),
within = elemList.indexOf(elem),
touching = [];
if (within !== -1) {
elemList.splice(within, 1);
}
for (var i = 0; i < elemList.length; i++) {
if (detectOverlap(elem, elemList[i])) {
touching.push(elemList[i].id);
}
}
if (touching.length) {
console.log(elem.id + ' touches ' + touching.join(' and ') + '.');
alert(elem.id + ' touches ' + touching.join(' and ') + '.');
} else {
console.log(elem.id + ' touches nothing.');
alert(elem.id + ' touches nothing.');
}
}
this is my video game right now (please do not copy)
<!DOCTYPE html>
/
<html>
<form id="player" class="box">
</form>
<button type="button" class="up" onclick="moveup()">^</button>
<button type="button" class="down" onclick="movedown()">v
</button>
<style src="style.css">
#player {
width: 300px;
height: 100px;
background-color: blue;
display: inline-block;
position: relative;
bottom: -250px;
left: 200px;
}
.up {
position: relative;
bottom: -400px;
}
.down {
position: relative;
bottom: -420px;
}
body {
background-color: black;
}
#car {
width: 300px;
height: 100px;
background-color: red;
display: inline-block;
position: relative;
bottom: -250px;
left: 600px;
}
</style>
<form id="car" class="box"></form>
<script>
imgObj = document.getElementById('player');
imgObj.style.position= 'relative';
imgObj.style.bottom = '-250px';
function moveup() {
imgObj.style.position= 'relative';
imgObj.style.bottom = '-250px';
imgObj.style.bottom = parseInt(imgObj.style.bottom) + 70 + 'px';
}
function movedown() {
imgObj.style.position= 'relative';
imgObj.style.bottom = '-250px';
imgObj.style.bottom = parseInt(imgObj.style.bottom) + -120 + 'px';
}
myMove();
function myMove() {
var elem = document.getElementById("car");
var pos = 0;
var id = setInterval(frame, 5);
function frame() {
if (pos == 1000) {
clearInterval(id);
myMove();
} else {
pos++;
elem.style.left = pos + "px";
elem.style.left = pos + "px";
}
}
}
/* please do not copy; this is it so far i want the red box when it hits the player(blue box) to stop everything that is happening */
/* made by Jsscripter; car game */
</script>
</html>
Intersection observer. API was largely developed because of news feeds and infinite scrolling. Goal was to solve when something comes into view, load content. Also is a great fit for a game.
The Intersection Observer API lets code register a callback function
that is executed whenever an element they wish to monitor enters or
exits another element (or the viewport), or when the amount by which
the two intersect changes by a requested amount. This way, sites no
longer need to do anything on the main thread to watch for this kind
of element intersection, and the browser is free to optimize the
management of intersections as it sees fit.
https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
All major browsers except safari support the API. For backwards compatibility and Safari support can use the polyfill from W3C found here. Check out this example from MDN:
var callback = function(entries, observer) {
entries.forEach(entry => {
// Each entry describes an intersection change for one observed
// target element:
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.target
// entry.time
});
};
var options = {
root: document.querySelector('#scrollArea'),
rootMargin: '0px',
threshold: 1.0
}
var observer = new IntersectionObserver(callback, options);
var target = document.querySelector('#listItem');
observer.observe(target);
See this in action here: https://codepen.io/anon/pen/OqpeMV

HTML5 Drag and Drop - How to remove the default ghost image on IE

I have implemented the drag and drop API of HTML5 in my app, and I need to disable the default ghost image when the user drag an item.
The items aren't images but row from a table like :
<table>
<tr draggable droppable ><td></td></tr>
<tr draggable droppable ><td></td></tr>
<tr draggable droppable ><td></td></tr>
</table>
Each row can be draggable in to another one (see it as a filesystem with folder and files).
In my dragstart I've done something like this to hide the default ghost image :
e.originalEvent.dataTransfer.setDragImage(disableImg[0], 0, 0);
Where diableImg is a dom element with 0 width and 0 height opacity 0 etc...
The issue here is that this is not working for IE since it doesn't support the setDragImage.
Is there another way to disable this ghost image of my row on drag ?
I just need to have it working from IE 11 -> Edge.
Thanks.
Example of set custom ghost image in IE.
var ghostImg = document.getElementById("ghostImg");
document.getElementById('dragme').addEventListener('dragstart', function(e) {
var target = e.srcElement || e.target;
var cloneNode = target.cloneNode(true);
target.parentNode.insertBefore(cloneNode, target);
target.style.display = "none";
window.setTimeout(function() {
target.parentNode.removeChild(cloneNode);
target.style.display = "block";
}, 0);
ghostImg.style.zIndex = '99';
ghostImg.style.visibility = 'visible'
ghostImg.style.top = e.clientY + 'px';
ghostImg.style.left = e.clientX + 'px';
})
document.getElementById('dragme').addEventListener('drag', function(e) {
ghostImg.style.zIndex = '99';
ghostImg.style.top = e.clientY + 'px';
ghostImg.style.left = e.clientX + 'px';
});
document.getElementById('dragme').addEventListener('dragend', function(e) {
ghostImg.removeAttribute('style');
});
#dragme {
width: 100px;
height: 100px;
background: #78ebff;
border: 1px solid #56bdff;
text-align: center;
line-height: 100px;
border-radius: 10px;
}
#ghostImg {
position: fixed;
left: 0;
top: 0;
z-index: -1;
visibility: hidden;
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="dragme" draggable="true">
Drag me
</div>
<img id="ghostImg" src="data:image/png;base64,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"/>
</body>
</html>
On dragStart event, implement:
function dragStart(e)
{
var target = e.srcElement || e.target;
if (isIEBrowser()) // check its IE browser
{
var cloneNode = target.cloneNode(true);
target.parentNode.insertBefore(cloneNode, target);
target.style.visibility = "collapse";
window.setTimeout(() => {
target.parentNode.removeChild(cloneNode);
target.style.visibility = "visible";
}, 0);
}
}
I was having this problem as well. I simply set the display to none before the drag.
if (event.dataTransfer.setDragImage)
{
let dragImage = document.createElement("div");
dragImage.style.visibility = "hidden";
event.dataTransfer.setDragImage(dragImage, 0, 0);
}
else
{
//Well if we cannot remove the ghost image then we need to make the initial image invisible when a ghost image would be captured
//then make the item visible again.
var initialDisplay = event.srcElement.style.display;
event.srcElement.style.display = "none";
window.setTimeout(() =>
{
event.srcElement.style.display = initialDisplay;
});
}
Seems after 1 frame (notice the settimeout doesn't specify a delay time) the display went back to normal, but when the ghost image was captured it was invisible.

Listening for addEventListener calls for a custom event

I'm trying to build a custom drag event. Here's my initial pseudocode, note that I left event cleaning up out.
var dragEvent = new CustomEvent("drag");
var anArbitrairyElement = document.querySelector(".box");
anArbitrairyElement.addEventListener("drag", function () {
console.log("event received");
});
(function () {
var dragEventListeners = [];
window.addEventListener("mousedown", function (mousedownEvent) {
dragEventListeners.forEach(function (target) {
if (mousedownEvent.target === target) {
window.addEventListener("mousemove", function (mousemoveEvent) {
target.dispatchEvent(dragEvent);
});
}
// ...
});
});
// does something like this exist?
onDragEventAdded(function (listenerElement) {
dragEventListeners.push(listenerElement);
});
}());
Is there any way I can listen to addEventListener calls without overwriting the addEventListener function itself? The solution needs to end-up in this being possible:
document.querySelector(".someElement").addEventListener("drag", ...);
Otherwise, is there another way how I could achieve the desired behavior of creating a custom drag event?
Do it the following way.
window.addEventListener("mousedown", function (mousedownEvent) {
var mouseMoveHandler =
function(element) {
return function(mouseMoveEvent) {
element.dispatchEvent(dragEvent);
}
}(mousedownEvent.target);
window.addEventListener("mousemove", mouseMoveHandler);
});
});
So in this case on mousedown event you create closure that will trigger drag event on the clicked element. You do not need the array of elements that were clicked. Clicked element is already injected to the handler.
Don't forget to clean listeners on mouseup. Just drop the mousemove listener on window
For anyone who wants to use the drag event, here's an example utilizing it.
var box1 = document.createElement("div");
var box2 = document.createElement("div");
var box3 = document.createElement("div");
document.body.appendChild(box1);
document.body.appendChild(box2);
document.body.appendChild(box3);
box1.className = "box";
box2.className = "box";
box3.className = "box";
box1.innerHTML = "Drag me";
box2.innerHTML = "No drag";
box3.innerHTML = "Drag me";
function dragElement(event) {
event.target.style.top = parseInt(event.target.style.top.split("px")[0] || 0) + event.dy + "px";
event.target.style.left = parseInt(event.target.style.left.split("px")[0] || 0) + event.dx + "px";
}
box1.addEventListener("drag", dragElement);
box3.addEventListener("drag", dragElement);
// custom event logic starting here
var dragEvent = new CustomEvent("drag");
window.addEventListener("mousedown", function (mousedownEvent) {
var mousePosition = {x: mousedownEvent.clientX, y: mousedownEvent.clientY};
(function () {
var target = mousedownEvent.target;
console.log(target);
function moveHandler(event) {
var newMousePosition = {x: event.clientX, y: event.clientY};
dragEvent.dx = newMousePosition.x - mousePosition.x;
dragEvent.dy = newMousePosition.y - mousePosition.y;
dragEvent.clientX = event.clientX;
dragEvent.clientY = event.clientY;
target.dispatchEvent(dragEvent);
mousePosition = newMousePosition;
}
function releaseHandler() {
window.removeEventListener("mousemove", moveHandler);
window.removeEventListener("mouseup", releaseHandler);
}
window.addEventListener("mousemove", moveHandler);
window.addEventListener("mouseup", releaseHandler);
}());
});
.box {
width: 75px;
height: 75px;
background-color: skyblue;
display: inline-block;
margin: 10px;
position: relative;
text-align: center;
font-family: helvetica;
color: white;
vertical-align: middle;
line-height: 75px;
font-weight: bold;
}

Interact with the tooltip in jQuery

How can I interact with the tooltip in jQuery?
You know, the little pop-up appearing when you hover an <a> element or an <img>.
I wanted to make that one follow my cursor when I move onto that tag. Exactly like this.
You might wanna look at jQuery UI's tooltip or the QTip plugin.
A part for mouse tracking tooltip: Mouse tracking
I didn't not tried it but it seems nice: one more
Here is simple jquery plugin for custom tooltip. jsFiddle
You can specify mouseFollow: true to achieve movable tooltip that follows cursor.
JS
(function ($) {
$.fn.tooltip = function (options) {
var defaults = {
background: '#fff',
border: '1px solid #999',
color: 'black',
rounded: false,
mouseFollow: false,
textChangeOnClick: false
},
settings = $.extend({}, defaults, options);
this.each(function () {
var $this = $(this),
title = null,
hovering = null;
//set class
if (!settings.textChangeOnClick) {
$this.addClass('_tooltip');
}
$(this).data('title', $this.attr('title'))
$(this).attr('title', '');
$this.hover(
// mouse over
function (e) {
//check change
if ($this.attr('title') != '') {
if ($this.attr('title') != $this.data('title')) {
$this.data('title', $this.attr('title'));
$this.attr('title','');
}
} else {
$this.removeAttr('title');
}
$this.attr('title', '');
hovering = true;
$('#tooltip').remove();
//create box
if ($this.data('title') != '') {
$('<div id="tooltip" />')
.appendTo('body')
.text($this.data('title'))
.hide()
.css({
backgroundColor: settings.background,
color: settings.color,
border: settings.border,
top: e.pageY + 10,
left: e.pageX + 20
})
.fadeIn(500);
}
if (settings.rounded) {
$('#tooltip').addClass('rounded');
}
},
//mouse out
function () {
hovering = false;
$('#tooltip').remove();
});
//text change
if (settings.textChangeOnClick) {
//on click
$this.on('click', function () {
if (hovering) {
//set new
$this.data('title',$(this).attr('title'));
$(this).attr('title', '');
$('#tooltip').text($this.data('title'));
}
});
}
//mouse follow
if (settings.mouseFollow) {
$this.mousemove(function (e) {
$('#tooltip').css({
top: e.pageY + 10,
left: e.pageX + 20
});
});
}
});
return this;
}
})(jQuery);
SET PLUGIN FOR ELEMENT
$('a').tooltip({
mouseFollow: true
});
HTML
CSS
#tooltip
{
border: 1px solid #BFBFBF;
float: left;
font-size: 11px;
max-width: 250px;
padding: 5px;
position: absolute;
color: #464646;
z-index: 999999;
}
.rounded
{
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
}

Categories