Skip to content
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

Add support for passing event options #3769

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions compat/src/render.js
Expand Up @@ -153,6 +153,11 @@ options.vnode = vnode => {
i = 'onfocusin';
} else if (/^onblur$/i.test(i)) {
i = 'onfocusout';
}
// See: https://github.com/facebook/react/blob/54f0e0f7308b4d0d51e52e149d3f7e5a207991ee/packages/react-dom-bindings/src/events/DOMPluginEventSystem.js#L432-L447
else if (/^on(Touch[MS]|Wh)/.test(i)) {
i = i.toLowerCase();
value = [value, { passive: true, capture: /Capture$/.test(i) }];
} else if (/^on(Ani|Tra|Tou|BeforeInp|Compo)/.test(i)) {
i = i.toLowerCase();
} else if (nonCustomElement && CAMEL_PROPS.test(i)) {
Expand Down
45 changes: 43 additions & 2 deletions compat/test/browser/events.test.js
Expand Up @@ -175,13 +175,19 @@ describe('preact/compat events', () => {
expect(proto.addEventListener.args.length).to.eql(4);
expect(proto.addEventListener.args[0].length).to.eql(3);
expect(proto.addEventListener.args[0][0]).to.eql('touchstart');
expect(proto.addEventListener.args[0][2]).to.eql(false);
expect(proto.addEventListener.args[0][2]).to.eql({
passive: true,
capture: false
});
expect(proto.addEventListener.args[1].length).to.eql(3);
expect(proto.addEventListener.args[1][0]).to.eql('touchend');
expect(proto.addEventListener.args[1][2]).to.eql(false);
expect(proto.addEventListener.args[2].length).to.eql(3);
expect(proto.addEventListener.args[2][0]).to.eql('touchmove');
expect(proto.addEventListener.args[2][2]).to.eql(false);
expect(proto.addEventListener.args[2][2]).to.eql({
passive: true,
capture: false
});
expect(proto.addEventListener.args[3].length).to.eql(3);
expect(proto.addEventListener.args[3][0]).to.eql('touchcancel');
expect(proto.addEventListener.args[3][2]).to.eql(false);
Expand Down Expand Up @@ -215,6 +221,41 @@ describe('preact/compat events', () => {
expect(proto.removeEventListener.args[3][2]).to.eql(false);
});

it('should makie touchstart, touchemove and wheel events passive', () => {
const onTouchStart = sinon.spy();
const onTouchMove = sinon.spy();
const onWheel = sinon.spy();

render(
<div
onTouchStart={onTouchStart}
onTouchMove={onTouchMove}
onWheel={onWheel}
/>,
scratch
);

scratch.firstChild.dispatchEvent(createEvent('touchstart'));
scratch.firstChild.dispatchEvent(createEvent('touchmove'));
scratch.firstChild.dispatchEvent(createEvent('wheel'));

expect(proto.addEventListener.args[0][0]).to.eql('touchstart');
expect(proto.addEventListener.args[0][2]).to.eql({
passive: true,
capture: false
});
expect(proto.addEventListener.args[1][0]).to.eql('touchmove');
expect(proto.addEventListener.args[1][2]).to.eql({
passive: true,
capture: false
});
expect(proto.addEventListener.args[2][0]).to.eql('wheel');
expect(proto.addEventListener.args[2][2]).to.eql({
passive: true,
capture: false
});
});

it('should support onTransitionEnd', () => {
const func = sinon.spy(() => {});
render(<div onTransitionEnd={func} />, scratch);
Expand Down
9 changes: 7 additions & 2 deletions src/diff/props.js
Expand Up @@ -54,7 +54,7 @@ function setStyle(style, key, value) {
* @param {boolean} isSvg Whether or not this DOM node is an SVG node or not
*/
export function setProperty(dom, name, value, oldValue, isSvg) {
let useCapture;
let useCapture, eventOptions;

o: if (name === 'style') {
if (typeof value == 'string') {
Expand Down Expand Up @@ -90,12 +90,17 @@ export function setProperty(dom, name, value, oldValue, isSvg) {
else name = name.slice(2);

if (!dom._listeners) dom._listeners = {};
if (!useCapture && Array.isArray(value)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens here when the user changes from passive to active? We would need to re-register the event-handler which might be unexpected that we aren't doing that. It did make me wonder whether the -capture would be a good fit for the -passive as well. The only other options are once and signal which I am not sure would fit here reliably....

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JoviDeCroock According to MDN only the capture option is checked for matching event listeners. See: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener#matching_event_listeners_for_removal

eventOptions = value[1];
value = value[0];
useCapture = eventOptions.capture || useCapture;
}
dom._listeners[name + useCapture] = value;

if (value) {
if (!oldValue) {
const handler = useCapture ? eventProxyCapture : eventProxy;
dom.addEventListener(name, handler, useCapture);
dom.addEventListener(name, handler, eventOptions || useCapture);
}
} else {
const handler = useCapture ? eventProxyCapture : eventProxy;
Expand Down