Add CSSOM View extensions to MouseEvent#3484
Conversation
| optional EventTarget? relatedTargetArg = null); | ||
| }; | ||
|
|
||
| // https://www.w3.org/TR/cssom-view/#extensions-to-the-mouseevent-interface |
There was a problem hiding this comment.
Use https://drafts.csswg.org/cssom-view/ here and below; we don't use TR URLs.
| }; | ||
|
|
||
| // https://www.w3.org/TR/cssom-view/#extensions-to-the-mouseevent-interface | ||
| // Changes existing coordinate entries to doubles |
There was a problem hiding this comment.
I'm not sure how well this works in webidl2js. I guess it didn't break the build, which is good, but does it properly "override" the existing ones?
I think before merging, I need a test to ensure that these correctly come out as doubles instead of longs. So, construct a new MouseEvent with its screenX/screenY/clientX/clientY constructor options set to something fractional (e.g. 1.5), and then read off the results from the object and ensure they're still fractional.
Bonus points if you add a test that using initMouseEvent does not preserve the fractionalness, since the arguments are still defined to be longs.
There was a problem hiding this comment.
Interestingly, it seems that although major browsers (chromium/firefox/webkit) have implemented these MouseEvent properties, they still convert doubles to longs (possibly for legacy compatibility reasons?) I don't know if that means jsdom shouldn't support doubles, but perhaps something to think about.
There was a problem hiding this comment.
Added tests around this
| get y() { | ||
| return this.clientY; | ||
| } | ||
| get offsetX() { |
There was a problem hiding this comment.
Per spec these should be the same as pageX/pageY. A test, or enabling of an existing test, would be good.
| get offsetY() { | ||
| return 0; | ||
| } | ||
|
|
There was a problem hiding this comment.
You're missing getters for pageX and pageY. See how they are defined in steps 2-3 of https://drafts.csswg.org/cssom-view/#dom-mouseevent-pagex .
There was a problem hiding this comment.
i'm not sure if the getters are actually needed, since (i think) by default the various impl properties are just regular properties (e.g. see clientX usage above, which works and doesn't have an explicit getter) Nevermind, I overlooked that bit of logic in the spec, I'll add them 👍
There was a problem hiding this comment.
Done, and added a test
* Add tests for long/double handling * Fully implement pageX/pageY/etc, add tests * Fix links in comments
| get pageX() { | ||
| if (this._dispatchFlag) { | ||
| return 0; | ||
| } |
There was a problem hiding this comment.
@jenseng @domenic I'm wondering why pageX returns 0 here when _dispatchFlag is true. This behavior leads to wrong results when accessing the event in a listener:
document.body.addEventListener('mousemove', e => {
console.log('event', e.pageX, e.pageY);
});
const ev = new MouseEvent('mousemove', { clientX: 10, clientY: 10 });
document.body.dispatchEvent(ev);Running this in a JSDOM environment logs event 0 0 because the pageX getter is called inside a dispatch.
On the other hand, running the same code in Chrome logs the correct event 10 10.
I see that the spec says things like "if the event’s dispatch flag is set, return A, otherwise return B", but that doesn't mean 0 is the right value. It should still be the coordinate of the event relative to some origin.
Looking at how Chrome handles this, I see that pageX is
- Initialized to
clientX - Incremented by a
scrollXoffset if available.
There is no mention of the dispatch flag, at least not explicitly. That leaves me with the conclusion that JSDOM shouldn't implement any special behavior when _dispatchFlag is true, always returning clientX + scrollX is fine.
This is quite a serious bug when JSDOM is used with Jest and React Testing Library, as it breaks unit tests that create MouseEvents to simulate moving the mouse.
I discovered this when trying out Jest upgraded to JSDOM 21 (jestjs/jest#13825) to run unit tests of WordPress Gutenberg project.
There was a problem hiding this comment.
@jsnajdr when implementing this, my goal was to:
- Follow the spec as closely as possible, and
- Return something reasonable where we can't fully implement it, i.e. since jsdom doesn't implement layout/rendering, some things are unknowable so perhaps 0 is the least bad option
A (minor) risk with returning the current clientX + scrollX is that it could be inconsistent with browsers, since scrollX/Y could change between the time of event initialization and when pageX/pageY are read during dispatch (e.g. if explicitly set or scrollTo is called during an earlier click handler).
That said, it's probably still a better default than zero, and assuming scrollX/Y hasn't changed, it should be synonymous with the position where the event occurred relative to the origin of the initial containing block.
@domenic wdyt? Seems like a reasonable change to me, though perhaps it's still a good idea to keep the dispatch branch even if the return value is the same. That way it's a good placeholder/indicator for when layout support is added.
There was a problem hiding this comment.
I do have an existing PR that is closely related to this, and I think we can solve the minor risk outlined above by setting the new _initialContainingBlockRelativeX/Y to clientX/Y + scrollX/Y.
That way the values will be persisted at init time, and not risk changing if scrollX/Y changes later before/during dispatch.
Maybe I'll just split it out into its own PR
Ensure that the CSSOM View MouseEvent attributes are set. Specifically this ensures that
x/ymirrorclientX/clientYand thatoffsetX/offsetYare set (always0since there is no layout).Fixes #3483, #3470
Builds on and supersedes #3472 (reworks it to fix the default case and the tests)