问题
I am simply trying to simulate a small click and drag on a draggable div elem — I've found several similar questions here on SO but all involving the use of additional plugins...
Is there a plain JavaScript or jQuery ability to handle specifically the drag? as I know .click();
or mouse down can be called to initiate.
I am not trying to create the ability of a drag and drop, I already have that. I am trying to create a small function that simulates this event automatically. click > hold > drag up 3 pixels
Update: Cannot find anything about this on SO or elsewhere without using a third party library, so creating a 500 bounty on it. Surely it's possible.
回答1:
You can simulate mouse events using MouseEvent interface and drag events using the DragEvent interface. You have to trigger the right sequence using EventTarget.dispatchEvent().
I guess that you combine "HTML Drag and Drop API" with "Mouse events" to create a drag and drop behaviour, so I'll provide one to simulate Drag & Drop and one to simulate Mouse capture and Mouse move.
Simulating Mouse capture and Mouse move
Please read inline comments
// We create 3 mouse events using MouseEvent interface,
// one for mousedown to initiate the process,
// one for mousemove to handle to movement
// and one for mouseup to terminate the process
const mouseDownEvent = new MouseEvent('mousedown', {
clientX: element.getBoundingClientRect().left,
clientY: element.getBoundingClientRect().top,
bubbles: true,
cancelable: true
});
const mouseMoveEvent = new MouseEvent('mousemove', {
clientX: element.getBoundingClientRect().left + 3,
clientY: element.getBoundingClientRect().top,
bubbles: true,
cancelable: true
});
const mouseUpEvent = new MouseEvent('mouseup', {
bubbles: true,
cancelable: true
});
// Dispatch the mousedown event to the element that has the listener
element.dispatchEvent(mouseDownEvent);
// For mousemove, the listener may be the parent or even the document
<element|document>.dispatchEvent(mouseMoveEvent);
// Dispatch mouseup to terminate the process
element.dispatchEvent(mouseUpEvent);
Run the code snippet. The square div element is draggable and you can click and drag it when the simulation timer is not running. Click the simulate button to see the simulation in action:
// Timer ID holder
let linearSimulationTimer;
// Simulation X/Y calculation
let calcX = 0, calcY = 0;
// Simulation X/Y axis orientation to handle parent collisions
let xAxisOrientation = 1, yAxisOrientation = 1;
// How many pixels to move the element for X/Y axis
const pixelsShift = 3;
// Elements
const simulateButton = document.getElementById('simulate-dnm');
const movable = document.getElementById('movable');
const movableContainer = movable.parentNode;
simulateButton.addEventListener('click', function() {
// If there is a timer running
if (linearSimulationTimer) {
// Stop and clear the timer
clearInterval(linearSimulationTimer);
linearSimulationTimer = null;
// Create a simple mouseup event with no custom properties
const mouseUpEvent = new MouseEvent('mouseup', {
bubbles: true,
cancelable: true,
});
// Dispatch it to the movable element
movable.dispatchEvent(mouseUpEvent);
// Handle button label text (start/stop)
simulateButton.classList.remove('running');
// Else if there is no timer running
} else {
// Create the mousedown event using movable element client left/top for event clientX.clientY
const mouseDownEvent = new MouseEvent('mousedown', {
clientX: movable.getBoundingClientRect().left,
clientY: movable.getBoundingClientRect().top,
pageX: 0,
pageY: 0,
bubbles: true,
cancelable: true,
view: window
});
// Dispatch the mousedown event to the movable element
movable.dispatchEvent(mouseDownEvent);
// Get movable parent client rect
const parentRect = movable.parentNode.getBoundingClientRect();
// Start the simulation timer
linearSimulationTimer = setInterval(() => {
// Get movable element size
const { width, height } = movable.getBoundingClientRect();
// Calculate new X/Y position and orientation
calcX += pixelsShift * xAxisOrientation;
calcY += pixelsShift * yAxisOrientation;
// If we hit movable parent X axis bounds, reverse X axis
if (calcX + width > parentRect.width) {
calcX = parentRect.width - width;
xAxisOrientation = -xAxisOrientation;
} else if (calcX < 0) {
calcX = 0;
xAxisOrientation = -xAxisOrientation;
}
// If we hit movable parent Y axis bounds, reverse Y axis
if (calcY + height > parentRect.height) {
calcY = parentRect.height - height;
yAxisOrientation = -yAxisOrientation;
} else if (calcY < 0) {
calcY = 0;
yAxisOrientation = -yAxisOrientation;
}
// Create mousemove event using calcX/calcY and the parent client position
const mouseMoveEvent = new MouseEvent('mousemove', {
clientX: parentRect.left + calcX,
clientY: parentRect.top + calcY,
pageX: 0,
pageY: 0,
bubbles: true,
cancelable: true,
view: window
});
// Dispatch the mousemove event to the parent which it has the listener
movableContainer.dispatchEvent(mouseMoveEvent);
}, 50);
// Handle button label text (start/stop)
simulateButton.classList.add('running');
}
});
// Mouse capture and drag handler (https://javascript.info/mouse-drag-and-drop)
movable.onmousedown = function(event) {
let shiftX = event.clientX - movable.getBoundingClientRect().left;
let shiftY = event.clientY - movable.getBoundingClientRect().top;
moveAt(event.pageX, event.pageY);
function moveAt(pageX, pageY) {
movable.style.left = pageX - shiftX - movableContainer.offsetLeft + 'px';
movable.style.top = pageY - shiftY - movableContainer.offsetTop + 'px';
}
function onMouseMove(event) {
moveAt(event.pageX, event.pageY);
}
movableContainer.addEventListener('mousemove', onMouseMove);
movable.onmouseup = function() {
movableContainer.removeEventListener('mousemove', onMouseMove);
movable.onmouseup = null;
}
}
movable.ondragstart = function() {
return false;
}
#movable-container {
position: relative;
height: 80px;
width: 200px;
margin: auto;
margin-bottom: 20px;
border: 1px dotted silver;
}
#movable {
position: relative;
left: 0;
width: 30px;
height: 30px;
background-color: cornflowerblue;
border-radius: 5px;
border: 1px solid grey;
}
#simulate-dnm > span:before {
content: 'Start ';
}
#simulate-dnm.running > span:before {
content: 'Stop ';
}
<div id="movable-container">
<div id="movable"></div>
</div>
<div>
<button id="simulate-dnm"><span>Mouse capture & move simulation</span></button>
</div>
Simulating Drag n Drop
Please read inline comments
// We create 3 drag events using DragEvent interface,
// one for dragstart to initiate the process,
// one for drop to handle the drag element drop inside the drop container
// and one for dragend to terminate the process
const dragStartEvent = new DragEvent('dragstart', {
bubbles: true,
cancelable: true
});
const dragEndEvent = new DragEvent('dragend', {
bubbles: true,
cancelable: true
});
const dropEvent = new DragEvent('drop', {
bubbles: true,
cancelable: true
});
// Dispatch the dragstart event to the source element to initiate
sourceNode.dispatchEvent(dragStartEvent);
// Dispatch the drop event to the destination element to get the drag
destinationNode.dispatchEvent(dropEvent);
// Dispatch the dragend event to the source element to finish
sourceNode.dispatchEvent(dragEndEvent);
Run the code snippet and hit the simulate button to trigger the drag n drop events sequence:
// The current parent index that the drag element is inside
let currentParentIndex = 0;
// Elements
const simulateButton = document.getElementById('simulate-dnd');
const sourceNode = document.getElementById('drag');
function simulateDragDrop(sourceNode, destinationNode) {
// Create dragstart event
const dragStartEvent = new DragEvent('dragstart', {
bubbles: true,
cancelable: true
});
// Create dragend event
const dragEndEvent = new DragEvent('dragend', {
bubbles: true,
cancelable: true
});
// Create drop event
const dropEvent = new DragEvent('drop', {
bubbles: true,
cancelable: true
});
// Dispatch dragstart event to the draggable element
sourceNode.dispatchEvent(dragStartEvent);
// Dispatch drop event to container element we want to drop the draggable
destinationNode.dispatchEvent(dropEvent);
// Dispatch dragend event to the draggable element
sourceNode.dispatchEvent(dragEndEvent);
}
simulateButton.addEventListener('click', function() {
// Change drop container index to other container than the current
const newParentIndex = currentParentIndex === 0 ? 1 : 0;
// Get the drop container element
const destinationNode = document.getElementsByClassName('dropzone')[newParentIndex];
// Initiate simulation sequence
simulateDragDrop(sourceNode, destinationNode);
// Save the new container index
currentParentIndex = newParentIndex;
});
// Drag n Drop handling
let dragged;
document.addEventListener("dragstart", function(event) {
// store a ref. on the dragged elem
dragged = event.target;
// make it half transparent
event.target.style.opacity = .5;
}, false);
document.addEventListener("dragend", function(event) {
// reset the transparency
event.target.style.opacity = "";
}, false);
/* events fired on the drop targets */
document.addEventListener("dragover", function(event) {
// prevent default to allow drop
event.preventDefault();
}, false);
document.addEventListener("dragenter", function(event) {
// highlight potential drop target when the draggable element enters it
if (event.target.className == "dropzone") {
event.target.style.background = "yellow";
}
}, false);
document.addEventListener("dragleave", function(event) {
// reset background of potential drop target when the draggable element leaves it
if (event.target.className == "dropzone") {
event.target.style.background = "";
}
}, false);
document.addEventListener("drop", function(event) {
// prevent default action (open as link for some elements)
event.preventDefault();
// move dragged elem to the selected drop target
if (event.target.className == "dropzone") {
event.target.style.background = "";
dragged.parentNode.removeChild(dragged);
event.target.appendChild(dragged);
currentParentIndex = Array.prototype.indexOf.call(event.target.parentNode.children, event.target);
}
}, false);
.dropzones {
display: flex;
justify-content: space-evenly;
}
.dropzone {
width: 100px;
height: 100px;
background-color: mintcream;
border-radius: 5px;
border: 2px dashed darkgrey;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 10px;
}
#drag {
margin: unset;
width: 40px;
height: 40px;
background-color: coral;
border-radius: 4px;
border: 1px solid grey;
}
<div class="dropzones">
<div class="dropzone">
<div id="drag" draggable="true"></div>
</div>
<div class="dropzone"></div>
</div>
<div>
<button id="simulate-dnd">Simulate Drag & Drop</button>
</div>
回答2:
If by 'simulate drag' you mean, trigger a mousedown event and then update the position of the element you should be able to do something like this:
let element = document.getElementById('draggable-div');
element.dispatchEvent(new Event('mousedown'));
element.style.top += 3
That style.top
change may or may not move the element depending on how it's positioned (absolute, relative, sticky).
If you have a drag and drop library that you are trying to trigger. Then you might even be able to simulate the drag by simply moving the element into the drop zone (3pixels up) and then firing a mouseup:
let element = document.getElementById('draggable-div');
element.style.top += 3
element.dispatchEvent(new Event('mouseup'));
And the list goes on since there also drag events now (though they are not fully supported yet by all browsers: https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API).
But it's not clear from your question what kind of simulation you are going for.
来源:https://stackoverflow.com/questions/61376278/simulate-a-3-pixel-drag-on-draggable-elem