prevent false positive detection of events that can be made passive - fixes #1914

pull/1929/head
Richard Harris 6 years ago
parent cd4f987f4e
commit 18c2ae715f

@ -585,7 +585,7 @@ export default class Element extends Node {
if (modifier === 'passive') {
if (passiveEvents.has(handler.name)) {
if (!handler.usesEventObject) {
if (handler.canMakePassive) {
component.warn(handler, {
code: 'redundant-event-modifier',
message: `Touch event handlers that don't use the 'event' object are passive by default`
@ -609,7 +609,7 @@ export default class Element extends Node {
}
});
if (passiveEvents.has(handler.name) && !handler.usesEventObject && !handler.modifiers.has('preventDefault')) {
if (passiveEvents.has(handler.name) && handler.canMakePassive && !handler.modifiers.has('preventDefault')) {
// touch/wheel events should be passive by default
handler.modifiers.add('passive');
}

@ -8,7 +8,8 @@ export default class EventHandler extends Node {
modifiers: Set<string>;
expression: Expression;
handler_name: string;
usesContext: boolean;
usesContext = false;
canMakePassive = false;
constructor(component: Component, parent, template_scope, info) {
super(component, parent, template_scope, info);
@ -19,6 +20,24 @@ export default class EventHandler extends Node {
if (info.expression) {
this.expression = new Expression(component, this, template_scope, info.expression);
this.usesContext = this.expression.usesContext;
if (/FunctionExpression/.test(info.expression.type) && info.expression.params.length === 0) {
// TODO make this detection more accurate — if `event.preventDefault` isn't called, and
// `event` is passed to another function, we can make it passive
this.canMakePassive = true;
} else if (info.expression.type === 'Identifier') {
let node = component.node_for_declaration.get(info.expression.name);
if (node && node.type === 'VariableDeclaration') {
// for `const handleClick = () => {...}`, we want the [arrow] function expression node
const declarator = node.declarations.find(d => d.id.name === info.expression.name);
node = declarator.init;
}
if (node && /Function/.test(node.type) && node.params.length === 0) {
this.canMakePassive = true;
}
}
} else {
const name = component.getUniqueName(`${this.name}_handler`);
component.declarations.push(name);

@ -0,0 +1,50 @@
/* generated by Svelte vX.Y.Z */
import { SvelteComponent as SvelteComponent_1, addListener, createElement, detachNode, identity, init, insert, noop, run, safe_not_equal } from "svelte/internal";
function create_fragment(component, ctx) {
var a, current, dispose;
return {
c() {
a = createElement("a");
a.textContent = "this should not navigate to example.com";
a.href = "https://example.com";
dispose = addListener(a, "touchstart", touchstart_handler);
},
m(target, anchor) {
insert(target, a, anchor);
current = true;
},
p: noop,
i(target, anchor) {
if (current) return;
this.m(target, anchor);
},
o: run,
d(detach) {
if (detach) {
detachNode(a);
}
dispose();
}
};
}
function touchstart_handler(e) {
return e.preventDefault();
}
class SvelteComponent extends SvelteComponent_1 {
constructor(options) {
super();
init(this, options, identity, create_fragment, safe_not_equal);
}
}
export default SvelteComponent;

@ -0,0 +1,3 @@
<a href="https://example.com" on:touchstart="{e => e.preventDefault()}">
this should not navigate to example.com
</a>

@ -8,8 +8,8 @@
}
</script>
<div on:touchstart="{handleTouchstart}">
<button on:click|stopPropagation|preventDefault="{handleClick}">click me</button>
<button on:click|once|capture="{handleClick}">or me</button>
<button on:click|capture="{handleClick}">or me!</button>
<div on:touchstart={handleTouchstart}>
<button on:click|stopPropagation|preventDefault={handleClick}>click me</button>
<button on:click|once|capture={handleClick}>or me</button>
<button on:click|capture={handleClick}>or me!</button>
</div>
Loading…
Cancel
Save