I am really confused about how preventDefault
function works. In the following example I expect when I drag elem2
, the drag event get cancelled and it does. But why the elem3
drag event is cancelled too when I drag elem3
?
Lets clarify more: When you drag a draggable element , the dragstart
event is fired on that element and all its ancestors. Here when you start dragging elem3
, because event capture is false
, the event bubbles up from elem3
to elem1
(a true
event capture just makes the order of firing reversed). So when you start dragging elem3
the event fires first on elem3
, then elem2
and finally elem1
. When there is no preventDefault
you can drag all three elements. When I add preventDefault
to elem2
, you cannot drag it anymore. But now I cannot drag elem3
either. Why? There is no preventDefault
in elem3
event handler!
const elem1 = document.getElementById("elem1");
elem1.addEventListener("dragstart", elem1EventHandler, false);
const elem2 = document.getElementById("elem2");
elem2.addEventListener("dragstart", elem2EventHandler, false);
const elem3 = document.getElementById("elem3");
elem3.addEventListener("dragstart", elem3EventHandler, false);
function elem1EventHandler(event) {
console.log("target: " + event.target.id);
console.log("currentTarget: " + event.currentTarget.id);
}
function elem2EventHandler(event) {
console.log("target: " + event.target.id);
console.log("currentTarget: " + event.currentTarget.id);
event.preventDefault();
}
function elem3EventHandler(event) {
console.log("target: " + event.target.id);
console.log("currentTarget: " + event.currentTarget.id);
}
#elem1 {
display: block;
position: relative;
width: 200px;
height: 150px;
background-color: deepskyblue;
}
#elem2 {
display: block;
width: 150px;
height: 100px;
background-color: greenyellow;
}
#elem3 {
display: block;
width: 100px;
height: 50px;
background-color: coral
}
<div id="elem1" draggable="true">
<div id="elem2" draggable="true">
<div id="elem3" draggable="true">
</div>
</div>
</div>
You have 2 stages with DOM events
focus
and you don't use capture). So methods like stopPropagation()
don't work to stop the default action.preventDefault()
in the propagation stage.By listening on a parent element you actually get both the parent's events and its children's events due event propagation/bubbling. To prevent the default action only for the parent check event.target
. Now you can drag the orange child:
const elem2 = document.getElementById("elem2");
elem2.addEventListener("dragstart", event => event.target === elem2 && event.preventDefault());
#elem1 {
display: block;
position: relative;
width: 200px;
height: 150px;
background-color: deepskyblue;
}
#elem2 {
display: block;
width: 150px;
height: 100px;
background-color: greenyellow;
}
#elem3 {
display: block;
width: 100px;
height: 50px;
background-color: coral
}
<div id="elem1" draggable="true">
<div id="elem2" draggable="true">
<div id="elem3" draggable="true">
</div>
</div>
</div>
Note that if you use function
as an event handler, this
refers to the DOM element you listen with the handler, so it could be written also like this:
document.getElementById("elem2").addEventListener("dragstart", function(event) {
event.target === this && event.preventDefault();
});
#elem1 {
display: block;
position: relative;
width: 200px;
height: 150px;
background-color: deepskyblue;
}
#elem2 {
display: block;
width: 150px;
height: 100px;
background-color: greenyellow;
}
#elem3 {
display: block;
width: 100px;
height: 50px;
background-color: coral
}
<div id="elem1" draggable="true">
<div id="elem2" draggable="true">
<div id="elem3" draggable="true">
</div>
</div>
</div>