Skip to content

[cssom-view] It is unclear when offsetX/Y are calculated #1070

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
smaug---- opened this issue Mar 1, 2017 · 22 comments
Open

[cssom-view] It is unclear when offsetX/Y are calculated #1070

smaug---- opened this issue Mar 1, 2017 · 22 comments

Comments

@smaug----
Copy link

smaug---- commented Mar 1, 2017

https://drafts.csswg.org/cssom-view/#extensions-to-the-mouseevent-interface

The spec says
"return the x[y]-coordinate of the position where the event occurred relative to the origin of the padding edge of the target node, ignoring the transforms that apply to the element and its ancestors, and terminate these steps."

blink and webkit cache the value at the first access, Edge seems to cache it when event is created and Gecko calculates it always. My reading is that Gecko does it right, bz' reading is that Edge does it right, but then, blink/webkit model is the fastest.

@bzbarsky
Copy link

bzbarsky commented Mar 1, 2017

@zcorpan ping.

@bzbarsky
Copy link

bzbarsky commented Mar 1, 2017

Specifically, consider the behavior of these three testcases in the various browsers:

First:

<div style="position: relative" id="mydiv">
  Click on the "C" of the first "Click" in this line
</div>
<script>
document.getElementById("mydiv").onclick = function(e) {
  console.log(e.offsetX);
  document.querySelector("div").style.left = "200px";
  console.log(e.offsetX);
};
</script>

For this testcase, Gecko reports a number near 0, then a number near -200. Blink/WebKit report a number near 0 twice. Edge 14 matches WebKit/Blink. Live version at https://jsfiddle.net/7x5e701m/1/

Second:

<div style="position: relative" id="mydiv">Click on the "C" of the first "Click" in this line</div>
<script>
document.getElementById("mydiv").onclick = function(e) {
  document.querySelector("div").style.left = "200px";
  console.log(e.offsetX);
};
</script>

For this testcase, both Gecko and Blink/WebKit report a single number near -200. Edge reports a number near 0. Live version at https://jsfiddle.net/7x5e701m/2/

Third:

<div style="position: relative" id="mydiv">
  Click on the "C" of the first "Click" in this line
</div>
<script>
document.getElementById("mydiv").onclick = function(e) {
  var d = document.querySelector("div");
  d.style.left = "200px";
  console.log(d.offsetLeft)
  console.log(e.offsetX);
};
</script>

For this testcase, both Gecko and Blink/WebKit report a number near 200, then a number near -200. Edge reports a number near 200, then a number near 0. Live version at https://jsfiddle.net/7x5e701m/4/

@zcorpan zcorpan self-assigned this Mar 1, 2017
@zcorpan
Copy link
Member

zcorpan commented Mar 1, 2017

So do we want the Blink/WebKit behavior?

@zcorpan
Copy link
Member

zcorpan commented Mar 1, 2017

cc @RByers

@zcorpan zcorpan changed the title It is unclear when offsetX/Y are calculated [cssom-view] It is unclear when offsetX/Y are calculated Mar 1, 2017
@smaug----
Copy link
Author

webkit/blink behavior is the most surprising, but performance is better.
But I think I could live with that.

@RByers
Copy link
Contributor

RByers commented Mar 2, 2017

@dtapuska and @mustaqahmed are probably the right people on the blink input team to comment on this.

@FremyCompany
Copy link
Contributor

I would note that these numbers should be easy to compute when you are doing your hit testing, which you need to do anyway to find the target. Except I recall Chrome does not do a lot of hit-testing for touch events so maybe in that case computing the number would be an extra effort?

@dtapuska
Copy link

dtapuska commented Mar 6, 2017

I'd prefer that offsetX and offsetY were calculated only at the Event creation time and were not layout inducing properties fields. Being that this has been shipped by a few browsers already I don't see why we would want to do the more expensive approach making these fields layout inducing.

@bzbarsky
Copy link

bzbarsky commented Mar 6, 2017

I'd prefer that offsetX and offsetY were calculated only at the Event creation time

OK, that's the Edge behavior as far as I can tell, right?

I think the Edge behavior is strange

Given your previous sentence, I'm wondering what you think the Edge behavior is....

I don't see why we would want to do the more expensive approach making these fields layout inducing

Just so we're clear: they're layout inducing in Blink right now, the first time you call the getter. You seem to be arguing they never should be, which means the behavior of everyone except Edge should change. This does not seem a priori terrible to me, but I'm not sure how to reconcile that with your second sentence.

@dtapuska
Copy link

dtapuska commented Mar 6, 2017

Let me clarify. I've removed my Edge comment that it is strange; it actually makes the most sense provided we can actually compute these values from the hit test.

Seems https://trac.webkit.org/changeset/82225 was the original change that introduced this behavior. And ultimately I think if (m_target == target) would have fixed the problem with the O(n^2) vs O(n) that the change was trying to address.

Chromium/WebKit reset the cached bool whenever the target changes for an event. Now a target is a readonly attribute so javascript can't set it although I believe the target changes when going into a shadow dom. So it could be possible that reading the cached value changes across the shadow DOM exhibits the "live" behavior FireFox has.

So granted it isn't as simple as determining it at hit testing time because the values depend on the target and the target might change across the shadow dom, right?

@bzbarsky
Copy link

bzbarsky commented Mar 6, 2017

Now a target is a readonly attribute so javascript can't set it

It can by explicitly doing dispatchEvent on it to some target, actually.

it isn't as simple as determining it at hit testing time because the values depend on the target and the target might change across the shadow dom, right

Ugh, yes, seems like it.

@FremyCompany
Copy link
Contributor

Wait, isn't the event bubbling path precomputed? It seems to be the case for elements that are not in a shadow root, if you move them during the event bubbling you do not change the elements that are being bubbled to.

https://jsfiddle.net/m0gdmuxq/

Edge does not support this but I would assume the same applies to Shadow DOM: if you move the shadow parent during its internal event phase you do not change the Light Tree node that will become the target. In which case you can still compute the values during hit testing, you just need to store them for the array of targets.

@bzbarsky
Copy link

bzbarsky commented Mar 7, 2017

The problem with shadow DOM is that you need to compute the values for the array of random things on the parent chain of the "actual" target. Things that you would normally not interact with at all during hit testing, probably.

@astearns astearns removed the Agenda+ label Mar 8, 2017
@astearns
Copy link
Member

astearns commented Mar 8, 2017

(removed agenda+ in favor of the current discussion here)

@smaug----
Copy link
Author

Looks like some browsers (webkit/blink ?) treat target.click() somehow as "simulated" and offsetX/Y is always 0. But then var e = new MouseEvent("click"); target.dispatchEvent(); isn't simulated and offsetX/Y isn't 0. It is hard to understand how click() is more simulated than create/dispatchEvent

That all feels very much ad-hoc. @dtapuska do you know what is the story here?

@dtapuska
Copy link

Looking at the code we always compute offsetX/Y but I think the definition of what we compute might be slightly different. Do you have a jsbin I can debug?

@dtapuska
Copy link

positionless mouse events are simulated events generated from other things like keyboard events/access keys. It may be that target.click() is likely generated in that same scenario.

@smaug----
Copy link
Author

smaug---- commented Mar 22, 2017

exactly. What are those "simulated events"? It seems like rather ad-hoc list of cases when event is simulated. If we want to have that behavior in the platform that offsetX/Y return 0 in some cases, better to spec in which cases. Or if we don't want to have that behavior, then need to file implementation bugs to remove that behavior.

and looking at the code, somehow positioned events state affects also to clientX/Y

@dtapuska
Copy link

So there are a few events that are generated position-less; mouseover,mousedown,mouseup,click.

Scenarios are:
HTML Element - click() call
A Link - keydown - Enter
Button - keyup - Space, keypress - Enter
SVG A LInk - keydown - Enter
Access Keys
Radio Input - Up/Down/Left/Right
Inputs - Enter
Summary Element - Keyup - Space, Keypress - Enter
Implicitly submitting a form - generates a click on the submit
Editable style Elements on keypress space or enter

How does Gecko know if the position of the event for these cases? screenX/Y, clientX/Y (along with offsetX/offsetY) all are 0 for these events that are generated as compat events (although layerX/Y don't seem to have these checks).

@dtapuska
Copy link

The layerX/layerY seem bogus in FireFox when you press 'Space' on this example:
http://jsbin.com/nuxusu/edit?html,js,output

@zcorpan zcorpan removed their assignment May 8, 2019
@noamr
Copy link
Collaborator

noamr commented Oct 23, 2020

Just bumped into this issue when trying to use the unfortunate property called MouseEvent.offsetX.

I think that WebKit/Blink behavior creates inconsistencies, and that the Gecko behavior is correct.
It's not in all cases "good for performance" to wait with computing offsetX/Y until they're requested, as this could lead in some cases to layout thrashing.

Envision the following case:

  • JS is handling a mouse move event
  • The handler changes the style, and affects the layout
  • Afterwards, the handler requests for offsetX
  • The browser needs to update the layout synchronously, to compute offsetX.
  • The handler makes more layout-affecting style changes

In the above case layout was performed twice for WebKit/Blink, and only once for Gecko.
The performance gain is only in cases where offsetX is not requested at all, and it can be achieved in other ways - namely to wait until layout is about to happen and then calculate it.

Regardless of performance, I would expect one of the following:

  • offsetX is the offset at the time of the event, and corresponds to clientX/pageX (as per spec)
  • offsetX is the offset every time it's requested (as it might change inside the handler)

But the Blink/WebKit behavior is neither! It is:

  • offsetX is the offset at the first time it is requested, and does not correspond to pageX/clientX.

So, IMO it is clear when offsetX/Y should be calculated, and the spec is correct.

moz-wptsync-bot pushed a commit to web-platform-tests/wpt that referenced this issue Mar 23, 2021
…nt is pressed

The click event dispatched for accesskey on other elements is marked as position-less event.
And other simulated click events are also marked as position-less, e.g. click() call, keydown Enter ...

label element should just behave the same on accesskey for consistency.

It is not clear on spec, see w3c/csswg-drafts#1070,
but blink/webkit both returns 0 for offset{X|Y}.

Differential Revision: https://phabricator.services.mozilla.com/D109451

bugzilla-url: https://bugzilla.mozilla.org/show_bug.cgi?id=1700241
gecko-commit: 20aee265bad1161a8269f6cdd30253afbb13cc7d
gecko-reviewers: masayuki
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue Mar 23, 2021
…of label element is pressed; r=masayuki

The click event dispatched for accesskey on other elements is marked as position-less event.
And other simulated click events are also marked as position-less, e.g. click() call, keydown Enter ...

label element should just behave the same on accesskey for consistency.

It is not clear on spec, see w3c/csswg-drafts#1070,
but blink/webkit both returns 0 for offset{X|Y}.

Differential Revision: https://phabricator.services.mozilla.com/D109451
moz-wptsync-bot pushed a commit to web-platform-tests/wpt that referenced this issue Mar 23, 2021
…nt is pressed

The click event dispatched for accesskey on other elements is marked as position-less event.
And other simulated click events are also marked as position-less, e.g. click() call, keydown Enter ...

label element should just behave the same on accesskey for consistency.

It is not clear on spec, see w3c/csswg-drafts#1070,
but blink/webkit both returns 0 for offset{X|Y}.

Differential Revision: https://phabricator.services.mozilla.com/D109451

bugzilla-url: https://bugzilla.mozilla.org/show_bug.cgi?id=1700241
gecko-commit: 20aee265bad1161a8269f6cdd30253afbb13cc7d
gecko-reviewers: masayuki
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants