[pointerevents] Clarify drag and drop events (#555)

sb3nder has just created a new issue for https://github.com/w3c/pointerevents:

== Clarify drag and drop events ==
Pointer events seem different for every combination of pointer input and browser,
so before filing bugs, I would like to know what is considered ok behavior.

First, I have two questions:

When the [Pointer Event spec says](https://w3c.github.io/pointerevents/#suppressing-a-pointer-event-stream):

> <p>The <a data-link-type="dfn" data-cite="infra" data-cite-path="" data-cite-frag="user-agent" href="https://infra.spec.whatwg.org/#user-agent">user agent<!---0.272262%--></a> <em class="rfc2119">MUST<!---0.272262%--></em> run the following steps to <a href="#dfn-suppress-a-pointer-event-stream" class="internalDFN" data-link-type="dfn" id="ref-for-dfn-suppress-a-pointer-event-stream-2">suppress a pointer event stream</a>:</p>
> <ul>
> <li>Fire a <!---0.272262%--><a data-link-type="idl" data-xref-type="attribute|dict-member|const" data-link-for="GlobalEventHandlers" data-xref-for="GlobalEventHandlers" href="#dfn-pointercancel" class="internalDFN" id="ref-for-dfn-pointercancel-2"><code>pointercancel<!---0.272262%--></code></a><!---0.272262%--> event.</li>
> <li>Fire a <!---0.272262%--><a data-link-type="idl" data-xref-type="attribute|dict-member|const" data-link-for="GlobalEventHandlers" data-xref-for="GlobalEventHandlers" href="#dfn-pointerout" class="internalDFN" id="ref-for-dfn-pointerout-4"><code>pointerout<!---0.272262%--></code></a><!---0.272262%--> event.</li>
> <li>Fire a <!---0.272262%--><a data-link-type="idl" data-xref-type="attribute|dict-member|const" data-link-for="GlobalEventHandlers" data-xref-for="GlobalEventHandlers" href="#dfn-pointerleave" class="internalDFN" id="ref-for-dfn-pointerleave-4"><code>pointerleave<!---0.272262%--></code></a><!---0.272262%--> event.</li>
> <li><a href="#dfn-implicitly-release-the-pointer-capture" class="internalDFN" data-link-type="dfn" id="ref-for-dfn-implicitly-release-the-pointer-capture-2">Implicitly release the pointer capture</a> if the pointer is currently captured.</li>
> </ul>

Is it fine even if pointerout and pointerleave aren't fired immediately, but after a while?

Why would the source node match `:active` and `:hover`, and what is the purpose of that?
I asked this in https://github.com/whatwg/html/issues/11771 , but maybe it's more of a CSS question.

<details>

<summary>Not knowing much, I would have expected something like this</summary>

```
pointerdown <DIV> section:active:hover div:active:hover
pointermove <DIV> section:active:hover div:active:hover
dragstart <DIV> section:active:hover div:active:hover
pointercancel <DIV> 
pointerout <DIV>
pointerleave <DIV>
pointerleave <SECTION>
dragenter <DIV>
...
dragleave <SECTION>
dragend <DIV>
```

</details>

---

Test page:
https://codepen.io/sb3nder/full/GgoEoLW
Structure:
```html
<section>
  <div draggable="true">drag me</div>
</section>
```
Step to reproduce:
drag div outside section.

---

## Chrome

<details>

<summary>mouse drag (desktop)</summary>

```
pointerdown <DIV> section:active:hover div:active:hover
pointermove <DIV> section:active:hover div:active:hover
dragstart <DIV> section:active:hover div:active:hover
pointercancel <DIV> section:active:hover div:active:hover
dragenter <DIV> section:active:hover div:active:hover
...
dragleave <SECTION> section:active:hover div:active:hover
dragend <DIV>
pointerout <DIV>
pointerleave <DIV>
pointerleave <SECTION>
```

</details>

---

<details>

<summary>mouse drag fast (desktop)</summary>

```
pointerdown <DIV> section:active:hover div:active:hover
pointerout <DIV> section:active:hover div:active
pointerleave <DIV> section:active:hover div:active
pointerover <SECTION> section:active:hover div:active
pointermove <SECTION> section:active:hover div:active
dragstart <DIV> section:active:hover div:active
pointercancel <SECTION> section:active:hover div:active
dragenter <SECTION> section:active:hover div:active
...
dragleave <SECTION> section:active:hover div:active
dragend <DIV>
pointerout <SECTION>
pointerleave <SECTION>
```

</details>

Source node doesn't match `:hover`.

---

</details>

<details>

<summary>touch drag (desktop)</summary>

```
pointerdown <DIV>
pointerdown <DIV>
gotpointercapture <DIV> section:active:hover div:active:hover
pointermove <DIV> section:active:hover div:active:hover
dragstart <DIV> section:active:hover div:active:hover
dragenter <DIV> section:active:hover div:active:hover
...
dragleave <SECTION> section:active:hover div:active:hover
pointercancel <DIV> section:active:hover div:active:hover
lostpointercapture <DIV> section:active:hover div:active:hover
pointerout <DIV> section:active:hover div:active:hover
pointerleave <DIV> section:active:hover div:active:hover
pointerleave <SECTION> section:active:hover div:active:hover
dragend <DIV>
```

</details>

`pointercancel` is fired after the DND operation, instead of before, as the [living spec states](https://html.spec.whatwg.org/#drag-and-drop-processing-model):

>10. <p><a id="drag-and-drop-processing-model:fire-a-pointer-event" href="https://w3c.github.io/pointerevents/#dfn-fire-a-pointer-event" data-x-internal="fire-a-pointer-event">Fire a pointer event</a> at the <a href="#source-node" id="drag-and-drop-processing-model:source-node-9">source node</a> named <code id="drag-and-drop-processing-model:event-pointercancel"><a data-x-internal="event-pointercancel" href="https://w3c.github.io/pointerevents/#the-pointercancel-event">pointercancel</a></code>, and fire any other follow-up events as required by <cite>Pointer Events</cite>. <a href="#refsPOINTEREVENTS">[POINTEREVENTS]</a></p>

I don't think `dragend` timing is right.

---

<details>

<summary>pen drag (desktop)</summary>

```
pointerdown <DIV> section:hover div:hover
gotpointercapture <DIV> section:hover div:hover
pointermove <DIV> section:hover div:hover
dragstart <DIV> section:active:hover div:active:hover
dragenter <DIV> section:active:hover div:active:hover
...
dragleave <SECTION> section:active:hover div:active:hover
dragend <DIV>
lostpointercapture <DIV>
pointerout <DIV>
pointerleave <DIV>
pointerleave <SECTION>
pointerout <DIV>
pointerleave <DIV>
pointerleave <SECTION>
```

</details>

`pointercancel` is not fired as the living spec states.

`pointerout` and `pointerleave` are fired a second time.

---

<details>

<summary>touch drag (Android)</summary>

```
pointerdown <DIV>
dragstart <DIV> section:active:hover div:active:hover
contextmenu <DIV> section:active:hover div:active:hover
gotpointercapture <DIV> section:active:hover div:active:hover
pointercancel <DIV> section:active:hover div:active:hover
lostpointercapture <DIV> section:active:hover div:active:hover
pointerout <DIV> section:active:hover div:active:hover
pointerleave <DIV> section:active:hover div:active:hover
pointerleave <SECTION> section:active:hover div:active:hover
dragenter <DIV> section:active:hover div:active:hover
dragover <DIV> section:active:hover div:active:hover
dragenter <SECTION> section:active:hover div:active:hover
dragleave <DIV> section:active:hover div:active:hover
dragover <SECTION> section:active:hover div:active:hover
dragleave <SECTION> section:active:hover div:active:hover
dragend <DIV>
```

</details>

This is more in line with what I would have expected.

---

## Firefox

<details>

<summary>mouse drag (desktop)</summary>

```
pointerdown <DIV> section:hover div:hover
pointermove <DIV> section:active:hover div:active:hover
dragstart <DIV> section:active:hover div:active:hover
pointercancel <DIV> section:active:hover div:active:hover
pointerout <DIV> section:active:hover div:active:hover
pointerleave <DIV> section:active:hover div:active:hover
pointerleave <SECTION> section:active:hover div:active:hover
pointerover <DIV> section:active:hover div:active:hover
pointerenter <SECTION> section:active:hover div:active:hover
pointerenter <DIV> section:active:hover div:active:hover
dragenter <DIV> section:active:hover div:active:hover
...
dragleave <SECTION> section:active:hover div:active:hover
dragend <DIV> section:active:hover div:active:hover
pointermove <DIV> section:active:hover div:active:hover
pointerout <DIV> section:active:hover div:active:hover
pointerleave <DIV> section:active:hover div:active:hover
pointerleave <SECTION> section:active:hover div:active:hover
```

</details>

Pointer events after `dragstart` are strange.

`:active` sticks.

---
<details>

<summary>mouse drag fast (desktop)</summary>

```
pointerdown <DIV> section:hover div:hover
dragstart <DIV> section:active:hover div:active:hover
pointercancel <DIV> section:active:hover div:active:hover
pointerout <DIV> section:active:hover div:active:hover
pointerleave <DIV> section:active:hover div:active:hover
pointerleave <SECTION> section:active:hover div:active:hover
pointerover <DIV> section:active:hover div:active:hover
pointerenter <SECTION> section:active:hover div:active:hover
pointerenter <DIV> section:active:hover div:active:hover
pointerout <DIV> section:active:hover div:active:hover
pointerleave <DIV> section:active:hover div:active:hover
pointerover <SECTION> section:active:hover div:active:hover
pointermove <SECTION> section:active:hover div:active:hover
dragenter <SECTION> section:active:hover div:active
...
dragleave <SECTION> section:active:hover div:active
dragend <DIV> section:active:hover div:active
pointermove <SECTION> section:active:hover div:active
pointerout <SECTION> section:active:hover div:active
pointerleave <SECTION> section:active:hover div:active
```

</details>

Source node doesn't match `:hover`.

---
<details>

<summary>touch drag (tap & hold)(desktop)</summary>

```
pointerdown <DIV>
gotpointercapture <DIV>
pointerup <DIV>
lostpointercapture <DIV>
pointerout <DIV>
pointerleave <DIV>
pointerleave <SECTION>
click <DIV> section:active:hover div:active:hover
pointerover <DIV> section:active:hover div:active:hover
pointerenter <SECTION> section:active:hover div:active:hover
pointerenter <DIV> section:active:hover div:active:hover
pointerdown <DIV> section:active:hover div:active:hover
dragstart <DIV> section:active:hover div:active:hover
dragenter <DIV> section:active:hover div:active:hover
...
dragleave <SECTION> section:active:hover div:active:hover
gotpointercapture <DIV> section:active:hover div:active:hover
pointercancel <DIV> section:active:hover div:active:hover
lostpointercapture <DIV> section:active:hover div:active:hover
pointerout <DIV> section:active:hover div:active:hover
pointerleave <DIV> section:active:hover div:active:hover
pointerleave <SECTION> section:active:hover div:active:hover
dragend <DIV>
```

</details>

`pointercancel` is fired after the DND operation.

I don't think `dragend` timing is right.

---

Please view or discuss this issue at https://github.com/w3c/pointerevents/issues/555 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Friday, 10 October 2025 11:09:01 UTC