Listening for addEventListener calls for a custom event - javascript

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;
}

Related

How Can Conflict Between Mouse Events Be Resolved

I have a div called starter-box that when clicked is supposed to dynamically create 2 divs. I added an event listener to the parent of the starter-box div to listen for any event of the 2 dynamically created divs.
The problem is that the click event listener conflicts with the mousedown event listener preventing it (the click event) from executing.
document.addEventListener('DOMContentLoaded', function () {
var container = document.querySelector("#container");
var target;
var elementCreated = false;
var elements = [];
container.addEventListener("click", (ev) => {
target = ev.target;
if (target.className != "box" && target.id != "container") {
if (elementCreated == false) {
createElements(target);
} else if (elementCreated == true) {
removeElementsByClass("box");
elements = [];
}
}
});
container.addEventListener("mousedown", (ev) => {
target = ev.target;
alert("MouseDown on: " + target.id);
});
container.addEventListener("mouseup", (ev) => {
target = ev.target;
alert("MouseUp on: " + target.id);
});
function createElements(target) {
const dimensions = target.getBoundingClientRect();
const element1 = document.createElement('div');
element1.className = "box";
element1.id = "box1";
element1.style.left = (dimensions.left) + "px";
element1.style.top = dimensions.bottom + 25 + "px";
container.appendChild(element1);
const element2 = document.createElement('div');
element2.className = "box";
element2.id = "box2";
element2.style.left = (dimensions.left) + "px";
element2.style.top = dimensions.bottom + 90 + "px";
container.appendChild(element2);
}
});
#container {
width: 600px;
height: 400px;
background-color: blue
}
#starter-box {
position: relative;
top: 50px;
left: 25px;
width: 50px;
height: 50px;
background-color: red
}
.box {
position: absolute;
width: 50px;
height: 50px;
background-color: black
}
<div id="container">
<div id="starter-box">
</div>
</div>
Check the fields of event object (ev argument to the listener callback).
In case of 'click' event: ev.type === 'click'
In case of 'mousedown' event: ev.type === 'mousedown'
You can use it your logic.
Discover the event object, try to console.log(ev), and see. There are more differences.

Automatic drag and drop with a click

I'm looking for an automatic drag and dropper. First, when I click anywhere on the screen, get coordinates, then drag and drop an element with the ID of "ball". using jQuery OR javascript.
I coded a similar script to what I want, but this script got patched when the website's client got updated. This one automatically dragged and dropped when I pressed key 1(keycode 49),
(function () {
'use strict';
var mouseX = 0;
var mouseY = 0;
var invName = '';
var timer = 0;
document.body.addEventListener('mousemove', function (e) {
mouseX = e.clientX;
mouseY = e.clientY;
});
$('.inventory-box').mousedown(function (e) {invName = e.currentTarget.id;});
function drop () {
$('#' + invName).trigger($.Event('mousedown', {button: 0}));
$('body').trigger($.Event('mouseup', {
button: 0,
clientX: mouseX,
clientY: mouseY
}));
timer = setTimeout(drop, 100);
}
window.addEventListener('keyup', function (e) {
if (e.keyCode == 49 && !timer) {
invName = 'ball';
drop();
setTimeout(function () {
(clearTimeout(timer), timer = 0);
}, 20);
}
});
})();
when I click anywhere on the screen, it gets it's coordinates, then drag and drops an element with the ID of "ball"
Here's a very simple vanilla JavaScript method that will locate an element with the ID of "ball" at the cursor location upon click.
The "ball" will follow the cursor until the next click, then the ball will be dropped at the click location.
const ball = document.getElementById('ball');
const ballHalfHeight = Math.round(ball.offsetHeight / 2);
const ballHalfWidth = Math.round(ball.offsetWidth / 2);
let dragState = false;
// move ball to position
function moveBallTo(x, y) {
ball.style.top = y - ballHalfHeight + 'px';
ball.style.left = x - ballHalfWidth + 'px';
}
// listen for 'mousemove' and drag ball
function dragListener(evt) {
const {clientX, clientY} = evt;
moveBallTo(clientX, clientY);
};
// respond to 'click' events (start or finish dragging)
window.addEventListener('click', (evt) => {
const {clientX, clientY} = evt;
moveBallTo(clientX, clientY);
ball.classList.remove('hidden');
// handle dragging
if (!dragState) {
window.addEventListener('mousemove', dragListener);
} else {
window.removeEventListener('mousemove', dragListener);
}
dragState = !dragState;
});
.div-ball {
position: fixed;
background-color: dodgerblue;
width: 2rem;
height: 2rem;
border-radius: 1rem;
}
.hidden {
opacity: 0;
}
<body>
<h4>Click anywhere</h4>
<div class="div-ball hidden" id="ball"></div>
</body>

Prevent a click event on parent when children are being dragged

I see a couple of similar questions. All of them say "call event.stopPropagtion()". I'm calling event.stopPropagation() but I'm still getting click events on parents. What am I doing wrong?
let dragTarget;
let dragMouseStartX;
let dragMouseStartY;
let dragTargetStartX;
let dragTargetStartY;
const px = v => `${v}px`;
function dragStart(e) {
e.preventDefault();
e.stopPropagation();
dragTarget = this;
const rect = this.getBoundingClientRect();
dragMouseStartX = e.pageX;
dragMouseStartY = e.pageY;
dragTargetStartX = (window.scrollX + rect.left) | 0;
dragTargetStartY = (window.scrollY + rect.top) | 0;
window.addEventListener('mousemove', dragMove, {passive: false});
window.addEventListener('mouseup', dragStop, {passive: false});
}
function dragMove(e) {
if (dragTarget) {
e.preventDefault();
e.stopPropagation();
const x = dragTargetStartX + (e.pageX - dragMouseStartX);
const y = dragTargetStartY + (e.pageY - dragMouseStartY);
dragTarget.style.left = px(x);
dragTarget.style.top = px(y);
}
}
function dragStop(e) {
e.preventDefault();
e.stopPropagation();
dragTarget = undefined;
window.removeEventListener('mousemove', dragMove);
window.removeEventListener('mouseup', dragStop);
}
document.querySelector('.drag').addEventListener('mousedown', dragStart);
document.body.addEventListener('click', () => {
console.log('body clicked', new Date());
});
body { height: 100vh; }
.drag {
background: red;
color: white;
padding: 1em;
position: absolute;
user-select: none;
cursor: pointer;
}
<div class="drag">drag and release me</div>
The problem is you're listening the mousedown event. clickevent and mousedownevent are different types of events, so to fix your code must add this:
document.querySelector('.drag').addEventListener('click', e => e.stopPropagation());
Doing this you're going to intercept the event in the children and you can handle it as you want.
let dragTarget;
let dragMouseStartX;
let dragMouseStartY;
let dragTargetStartX;
let dragTargetStartY;
const px = v => `${v}px`;
function dragStart(e) {
e.preventDefault();
e.stopPropagation();
dragTarget = this;
const rect = this.getBoundingClientRect();
dragMouseStartX = e.pageX;
dragMouseStartY = e.pageY;
dragTargetStartX = (window.scrollX + rect.left) | 0;
dragTargetStartY = (window.scrollY + rect.top) | 0;
window.addEventListener('mousemove', dragMove, {passive: false});
window.addEventListener('mouseup', dragStop, {passive: false});
}
function dragMove(e) {
if (dragTarget) {
e.preventDefault();
e.stopPropagation();
const x = dragTargetStartX + (e.pageX - dragMouseStartX);
const y = dragTargetStartY + (e.pageY - dragMouseStartY);
dragTarget.style.left = px(x);
dragTarget.style.top = px(y);
}
}
function dragStop(e) {
e.preventDefault();
e.stopPropagation();
dragTarget = undefined;
window.removeEventListener('mousemove', dragMove);
window.removeEventListener('mouseup', dragStop);
}
document.querySelector('.drag').addEventListener('mousedown', dragStart);
document.querySelector('.drag').addEventListener('click', e => e.stopPropagation());
document.body.addEventListener('click', () => {
console.log('body clicked', new Date());
});
body { height: 100vh; }
.drag {
background: red;
color: white;
padding: 1em;
position: absolute;
user-select: none;
cursor: pointer;
}
<div class="drag">drag and release me</div>

How to handle fast moving mouse which doesn't trigger mousemove event

I am trying to create a simple drag behavior for elements using mouse events and absolute positioning.
I have a mousemove event listener for the container div which changes the position of boxRef with the help of clientX and clientY properties of the mouse event.
It works fine when the mouse move is slow, but when it's fast, it just stops,
How can I handle this so that the element does not stop no matter the speed of the mouse move?
const shadowStytle = "10px 10px 12px #888888";
const appRef = document.getElementById('app');
appRef.innerHTML = `<div class="box" id="box"></div>`;
const boxRef = document.getElementById("box");
const xOffset = boxRef.clientWidth / 2;
const yOffset = boxRef.clientHeight / 2;
let selectionLocked = false;
function lockSelection() {
selectionLocked = true;
boxRef.style.boxShadow = shadowStytle;
boxRef.style.backgroundColor = "red";
}
function unlockSelection() {
selectionLocked = false;
boxRef.style.boxShadow = "none";
boxRef.style.backgroundColor = "black";
}
unlockSelection();
boxRef.addEventListener("mousedown", (arg) => {
lockSelection();
});
boxRef.addEventListener("mouseup", (arg) => {
unlockSelection();
});
boxRef.addEventListener("mouseleave", (arg) => {
if (selectionLocked) {
selectionLocked = false;
boxRef.style.boxShadow = "none";
}
});
appRef.addEventListener("mousemove", (arg) => {
if (selectionLocked) {
boxRef.style.left = `${arg.clientX - xOffset}px`;
boxRef.style.top = `${arg.clientY - yOffset}px`;
}
});
.box {
position: absolute;
border: 1px solid black;
padding: 25px;
width: 10%;
}
<div id="app"></div>
Stackblitz
You do not need the mouseleave on the element. Your problem is that that mouseleave fires on the element and it terminates your drag. Instead take advantage of the event bubbling and add your handler to a sufficiently large parent container or the window itself. Fire up mouseup on the window/container if and only if it has been previoulsy activated by mousedown. If you want you can also remove and re attach the handler on window on mousedown on the element if you want to save some computation. But I left it:
https://jsfiddle.net/ibowankenobi/gd1e93a3/1/
const shadowStytle = "10px 10px 12px #888888";
const appRef = document.getElementById('app');
appRef.innerHTML = `<div class="box" id="box"></div>`;
const boxRef = document.getElementById("box");
const xOffset = boxRef.clientWidth / 2;
const yOffset = boxRef.clientHeight / 2;
let selectionLocked = false;
function lockSelection() {
selectionLocked = true;
boxRef.style.boxShadow = shadowStytle;
boxRef.style.backgroundColor = "red";
}
function unlockSelection() {
selectionLocked = false;
boxRef.style.boxShadow = "none";
boxRef.style.backgroundColor = "black";
}
unlockSelection();
boxRef.addEventListener("mousedown", (arg) => {
lockSelection();
});
window.addEventListener("mouseup", (arg) => {
selectionLocked && unlockSelection();
});
boxRef.addEventListener("mouseleave", (arg) => {
/*if (selectionLocked) {
selectionLocked = false;
boxRef.style.boxShadow = "none";
}*/
});
window.addEventListener("mousemove", (arg) => {
if (selectionLocked) {
boxRef.style.left = `${arg.clientX - xOffset}px`;
boxRef.style.top = `${arg.clientY - yOffset}px`;
}
});

Div placement in javascript

I would like it to create the div where the mouse is. I have the following code:
var mouseisdown = false;
$(document).mousedown(function(event) {
mouseisdown = true;
doSomething();
}).mouseup(function(event) {
mouseisdown = false;
});
function doSomething(e){
var draw = document.createElement("div");
draw.className = "draw";
document.body.appendChild(draw);
draw.style.top = e.clientY + "px";
draw.style.left = e.clientX + "px";
if (mouseisdown)
doSomething();
}
Basically you already had it, but you overcomplicated it:
Remove the mouseisdown variable and the event listeners
Add doSomething as a click event listener
Don't call doSomething recursively
$(document).click(function doSomething(e){
var draw = document.createElement("div");
draw.className = "draw";
document.body.appendChild(draw);
draw.style.top = e.clientY + "px";
draw.style.left = e.clientX + "px";
});
.draw {
position: absolute;
height: 10px;
width: 10px;
margin: -5px;
background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Click somewhere

Categories