- Rohan Raja (roraja@microsoft.com)
- Chromium Bug: Issue 40068941
- Chromium Review: CL 6818116
- Spec: HTML5 Drag and Drop Specification
- Open new issue: Open New Issue
- Introduction
- User-Facing Problem
- Goals
- Non-goals
- Motivation
- Considered Alternatives
- Security and Privacy
- Performance Impact
- Interoperability
- References and Acknowledgements
The HTML5 Drag and Drop API allows web applications to handle drag-and-drop operations through a series of events: dragstart, dragenter, dragover, dragleave, drop, and dragend. During these events, the dataTransfer.dropEffect property indicates which operation (copy, move, link, or none) should be performed. According to the HTML5 specification, the dropEffect value set by web applications during the last dragover event should be preserved and available in the subsequent drop event.
However, prior to this fix, Chromium based browsers were overwriting the web application's dropEffect value with the browser's own negotiated operation before the drop event fired, breaking specification compliance and limiting developer control over drag-and-drop behavior.
Web developers building applications with drag-and-drop functionality need to:
- Determine which operation was requested by the user during
dragover(copy, move, or link) - Use this information in the
dropevent handler to perform the appropriate action - Provide consistent user feedback throughout the drag operation
Before this fix, the dropEffect property was being reset by the browser before the drop event, making it impossible for developers to reliably determine what operation should be performed. This led to:
const dropZone = document.getElementById('dropZone');
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
e.dataTransfer.dropEffect = 'copy'; // Developer sets "copy" operation
console.log('dragover dropEffect:', e.dataTransfer.dropEffect); // Logs: "copy"
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
console.log('drop dropEffect:', e.dataTransfer.dropEffect); // Before fix: "none" or unpredictable
// Developer cannot reliably determine which operation to perform
if (e.dataTransfer.dropEffect === 'copy') {
// This code path may not execute as expected
performCopyOperation(e.dataTransfer);
}
});This inconsistency meant:
- Developers couldn't implement spec-compliant drag-and-drop behavior
- Drop handlers couldn't determine the intended operation reliably
- User expectations (based on cursor feedback during drag) might not match the actual behavior during drop
- Custom drag-and-drop implementations were unreliable across different browsers
The goal of this feature is to ensure that the dropEffect value set by web applications during dragover event handlers is preserved and correctly reflected in the drop event, in compliance with the HTML5 specification. Specifically:
- Preserve Developer Intent: Maintain the
dropEffectvalue set by the web application during the lastdragoverordragenterevent through to thedropevent - Spec Compliance: Align browser behavior with the HTML5 specification requirement that states: "dropEffect will be set to the action that was desired, which will be the value dropEffect had after the last dragenter or dragover event"
- Enable Consistent Behavior: Allow developers to build reliable drag-and-drop interactions where the operation indicated during hover matches the operation available during drop
What developers can do with preserved dropEffect:
-
Implement Operation-Specific Drop Logic
- Differentiate between copy, move, and link operations in the drop handler
- Execute different code paths based on the user's intended operation
- Provide appropriate feedback and confirmations based on the operation
-
Build Consistent User Experiences
- Ensure the cursor feedback during drag matches the actual operation performed
- Implement visual indicators that accurately reflect what will happen on drop
- Build file managers, code editors, and other applications with reliable drag-and-drop
-
Comply with Platform Conventions
- Respect modifier keys (Ctrl/Cmd for copy, etc.) properly
- Match native application behavior for drag-and-drop operations
- Provide familiar interactions for users
This feature does not:
- Change the way
effectAllowedis negotiated or processed - Modify the cursor feedback during drag operations
- Alter the security model or permissions for drag-and-drop
- Add new drag-and-drop events or properties
- Change behavior for dragging between different windows or applications
The HTML5 specification clearly states that dropEffect should reflect the value set during the last dragover or dragenter event when the drop event fires. However, Chromium's implementation was overwriting this value with the browser's internal negotiated operation, breaking the specification and making it impossible for developers to build reliable drag-and-drop interactions.
This feature addresses:
- Spec Compliance: Aligns with the HTML5 Drag and Drop specification
- Developer Control: Returns control of the
dropEffectproperty to web applications - Predictable Behavior: Ensures the operation indicated during drag matches the operation available during drop
- Cross-Browser Consistency: Provides consistent behavior that developers can rely on
const dropZone = document.getElementById('dropZone');
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
e.dataTransfer.dropEffect = 'copy'; // Developer wants copy operation
console.log('dragover dropEffect:', e.dataTransfer.dropEffect); // Logs: "copy"
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
console.log('drop dropEffect:', e.dataTransfer.dropEffect); // Logs: "none" (WRONG!)
// Cannot determine the intended operation
if (e.dataTransfer.dropEffect === 'copy') {
performCopyOperation(e.dataTransfer); // This doesn't execute!
} else if (e.dataTransfer.dropEffect === 'move') {
performMoveOperation(e.dataTransfer);
}
});const dropZone = document.getElementById('dropZone');
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
e.dataTransfer.dropEffect = 'copy'; // Developer wants copy operation
console.log('dragover dropEffect:', e.dataTransfer.dropEffect); // Logs: "copy"
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
console.log('drop dropEffect:', e.dataTransfer.dropEffect); // Logs: "copy" (CORRECT!)
// Can now reliably determine the intended operation
if (e.dataTransfer.dropEffect === 'copy') {
performCopyOperation(e.dataTransfer); // This executes as expected!
} else if (e.dataTransfer.dropEffect === 'move') {
performMoveOperation(e.dataTransfer);
}
});// Example: Building a web-based file manager
const fileManager = document.getElementById('fileManager');
fileManager.addEventListener('dragover', (e) => {
e.preventDefault();
// Check for modifier keys and set appropriate operation
if (e.ctrlKey || e.metaKey) {
e.dataTransfer.dropEffect = 'copy';
} else if (e.shiftKey) {
e.dataTransfer.dropEffect = 'move';
} else if (e.altKey) {
e.dataTransfer.dropEffect = 'link';
} else {
e.dataTransfer.dropEffect = 'move'; // Default operation
}
// Update UI to show which operation will be performed
updateDropIndicator(e.dataTransfer.dropEffect);
});
fileManager.addEventListener('drop', (e) => {
e.preventDefault();
const files = Array.from(e.dataTransfer.files);
// NOW we can reliably use dropEffect to determine what to do
switch (e.dataTransfer.dropEffect) {
case 'copy':
copyFiles(files, e.target);
showNotification('Files copied');
break;
case 'move':
moveFiles(files, e.target);
showNotification('Files moved');
break;
case 'link':
createShortcuts(files, e.target);
showNotification('Shortcuts created');
break;
}
});This would continue to violate the HTML5 specification and limit developer capabilities. Developers would need to implement workarounds, such as:
- Storing the
dropEffectvalue in a global variable duringdragover - Using custom data attributes to track the intended operation
- Abandoning the native
dropEffectproperty entirely
Rejected because: This forces developers to work around the platform rather than use the standard API as designed.
We could expose both the developer-set value and the browser-negotiated value through separate properties (e.g., dataTransfer.requestedDropEffect and dataTransfer.actualDropEffect).
Rejected because: This adds complexity without clear benefit. The specification already defines the expected behavior, and adding new properties would create additional interoperability challenges.
We could make dropEffect read-only during the drop event to prevent confusion.
Rejected because: The specification allows reading dropEffect during drop, and making it read-only would break existing code that might check this value.
This proposal has no known impact on security or privacy:
- No New Data Exposure: The
dropEffectproperty already exists; this change only ensures its value is preserved correctly - No Permission Changes: The drag-and-drop security model remains unchanged
- Scoped to Web Content: This only affects how web applications receive the
dropEffectvalue; it does not change cross-origin or cross-application drag-and-drop behavior - User Control Maintained: Users still control the drag-and-drop operation through modifier keys and drop location
The feature simply ensures that the value web applications set is the value they receive, without introducing new security surfaces.
This feature introduces minimal overhead:
- No Additional Computation: The browser already tracks the
dropEffectvalue; this change only preserves it instead of overwriting it - Single Property Transfer: Only one additional property value needs to be passed from the
dragoverhandler to thedropevent - No New Allocations: No additional objects or data structures are created
This feature improves interoperability by:
- Aligning with the HTML5 Specification: Implements the behavior defined in the HTML5 Drag and Drop specification
- Reducing Browser Inconsistencies: Provides consistent behavior that developers can rely on across browsers
- Following Web Standards: Respects the intent of the specification that web applications should control the
dropEffectproperty
Web developers can now write drag-and-drop code that works consistently according to the specification, rather than working around browser-specific quirks.
- HTML5 Drag and Drop Specification
- Chromium Bug 40068941
- Chromium Code Review 6818116
- MDN: DataTransfer.dropEffect
Many thanks for valuable feedback and code reviews from: