Skip to content

Commit 7e05d12

Browse files
committed
Add EventListenerOptions and passive event listener feature
This introduces an EventListenerOptions dictionary which can be used to explicitly specify options to addEventListener and removeEventListener. This also introduces a "passive" option, which disables the ability for a listener to cancel the event. See https://github.com/RByers/EventListenerOptions/blob/gh-pages/explainer.md for a high-level overview.
1 parent 9202ada commit 7e05d12

File tree

2 files changed

+184
-73
lines changed

2 files changed

+184
-73
lines changed

dom.bs

+90-25
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ urlPrefix: https://html.spec.whatwg.org/multipage/
103103
text: effective script origin
104104
text: origin alias; url: #concept-origin-alias
105105
text: Unicode serialization of an origin
106+
urlPrefix: infrastructure.html
107+
text: in parallel
106108
urlPrefix: https://w3c.github.io/webcomponents/spec/shadow/
107109
type: dfn; urlPrefix: #dfn-
108110
text: shadow root
@@ -612,7 +614,7 @@ Lets look at an example of how <a>events</a> work in a <a>tree</a>:
612614
function test(e) {
613615
debug(e.target, e.currentTarget, e.eventPhase)
614616
}
615-
document.addEventListener("hey", test, true)
617+
document.addEventListener("hey", test, {capture: true})
616618
document.body.addEventListener("hey", test)
617619
var ev = new Event("hey", {bubbles:true})
618620
document.getElementById("x").dispatchEvent(ev)
@@ -727,16 +729,15 @@ inherits from the {{Event}} interface.
727729

728730
<dt><code><var>event</var> . <a method for=Event lt="preventDefault()">preventDefault</a>()</code>
729731
<dd>If invoked when the
730-
{{Event/cancelable}} attribute value is true,
732+
{{Event/cancelable}} attribute value is true, and while executing a listener
733+
for the <var>event</var> with {{EventListenerOptions/passive}} set to false,
731734
signals to the operation that caused <var>event</var> to be
732735
<a>dispatched</a> that it needs to be
733736
canceled.
734737

735738
<dt><code><var>event</var> . {{Event/defaultPrevented}}</code>
736739
<dd>Returns true if
737-
{{Event/preventDefault()}} was invoked
738-
while the {{Event/cancelable}} attribute
739-
value is true, and false otherwise.
740+
{{Event/preventDefault()}} was used successfully to indicate cancellation.
740741

741742
<dt><code><var>event</var> . {{Event/isTrusted}}</code>
742743
<dd>Returns true if <var>event</var> was
@@ -798,6 +799,7 @@ flags that are all initially unset:
798799
<li><dfn export for=Event>canceled flag</dfn>
799800
<li><dfn export for=Event>initialized flag</dfn>
800801
<li><dfn export for=Event>dispatch flag</dfn>
802+
<li><dfn export for=Event>passive flag</dfn>
801803
</ul>
802804

803805
The
@@ -816,7 +818,14 @@ must return the values they were initialized to.
816818
The
817819
<dfn method for=Event>preventDefault()</dfn>
818820
method must set the <a>canceled flag</a> if the
819-
{{Event/cancelable}} attribute value is true.
821+
{{Event/cancelable}} attribute value is true and
822+
the <a>passive flag</a> is unset.
823+
824+
<p class="note no-backref">
825+
User agents are encouraged to generate a console warning or other debugging
826+
aid to help authors identify places where calls to {{preventDefault()}}
827+
have no effect.
828+
</p>
820829

821830
The
822831
<dfn attribute for=Event>defaultPrevented</dfn>
@@ -971,14 +980,19 @@ for historical reasons.
971980
<pre class=idl>
972981
[Exposed=(Window,Worker)]
973982
interface EventTarget {
974-
void addEventListener(DOMString type, EventListener? callback, optional boolean capture = false);
975-
void removeEventListener(DOMString type, EventListener? callback, optional boolean capture = false);
983+
void addEventListener(DOMString type, EventListener? callback, optional (EventListenerOptions or boolean) options);
984+
void removeEventListener(DOMString type, EventListener? callback, optional (EventListenerOptions or boolean) options);
976985
boolean dispatchEvent(Event event);
977986
};
978987

979988
callback interface EventListener {
980989
void handleEvent(Event event);
981990
};
991+
992+
dictionary EventListenerOptions {
993+
boolean capture = false;
994+
boolean passive = false;
995+
};
982996
</pre>
983997

984998
{{EventTarget}} is an object to which an
@@ -990,34 +1004,43 @@ occurred. Each {{EventTarget}} has an associated list of
9901004
<p>An <dfn export id=concept-event-listener>event listener</dfn> can be used to observe a specific
9911005
<a>event</a>.
9921006

993-
<p>An <a>event listener</a> consists of a <b>type</b>, <b>callback</b>, and <b>capture</b>. An
1007+
<p>An <a>event listener</a> consists of a <b>type</b>, <b>callback</b>, <b>capture</b> and <b>passive</b>. An
9941008
<a>event listener</a> also has an associated <b>removed flag</b>, which is initially unset.
9951009

9961010
<p class="note no-backref">The callback is named {{EventListener}} for historical reasons. As can be
9971011
seen from the definition above, an <a>event listener</a> is a more broad concept.
9981012

9991013
<dl class=domintro>
1000-
<dt><code><var>target</var> . <a method lt="addEventListener()">addEventListener</a>(<var>type</var>, <var>callback</var> [, <var>capture</var> = false])</code>
1014+
<dt><code><var>target</var> . <a method lt="addEventListener()">addEventListener</a>(<var>type</var>, <var>callback</var> [, <var>options</var>])</code>
10011015
<dd>
10021016
Appends an <a>event listener</a> for <a>events</a> whose {{Event/type}} attribute value
10031017
is <var>type</var>. The <var>callback</var> argument sets the <b>callback</b> that will
1004-
be invoked when the <a>event</a> is <a>dispatched</a>. When set to true,
1005-
the <var>capture</var> argument prevents <b>callback</b> from being invoked when
1018+
be invoked when the <a>event</a> is <a>dispatched</a>.
1019+
1020+
The <var>options</var> argument sets listener-specific options. For compatibility this can be
1021+
just a boolean, in which case it determines the value of the <b>capture</b> option.
1022+
1023+
When set to true,
1024+
the <var>capture</var> option prevents <b>callback</b> from being invoked when
10061025
the <a>event</a>'s {{Event/eventPhase}} attribute value is {{Event/BUBBLING_PHASE}}.
10071026
When false, <b>callback</b> will not be invoked when <a>event</a>'s {{Event/eventPhase}}
10081027
attribute value is {{Event/CAPTURING_PHASE}}. Either way, <b>callback</b> will be
10091028
invoked if <a>event</a>'s {{Event/eventPhase}} attribute value is {{Event/AT_TARGET}}.
10101029

1030+
When set to true, the <var>passive</var> option indicates that the <b>callback</b>
1031+
will not cancel the event by invoking {{preventDefault()}}.
1032+
This is used to enable performance optimizations described in [[#observing-event-listeners]].
1033+
10111034
The <a>event listener</a> is appended to <var>target</var>'s list of
10121035
<a>event listeners</a> and is not appended if it is a duplicate, i.e., having the same
1013-
<b>type</b>, <b>callback</b>, and <b>capture</b> values.
1036+
<b>type</b>, <b>callback</b>, <b>capture</b> and <b>passive</b> values.
10141037

1015-
<dt><code><var>target</var> . <a method lt="removeEventListener()">removeEventListener</a>(<var>type</var>, <var>callback</var> [, <var>capture</var> = false])</code>
1038+
<dt><code><var>target</var> . <a method lt="removeEventListener()">removeEventListener</a>(<var>type</var>, <var>callback</var> [, <var>options</var>])</code>
10161039
<dd>Remove the <a>event listener</a>
10171040
in <var>target</var>'s list of
10181041
<a>event listeners</a> with the same
10191042
<var>type</var>, <var>callback</var>, and
1020-
<var>capture</var>.
1043+
<var>options</var>.
10211044

10221045
<dt><code><var>target</var> . <a method lt="dispatchEvent()">dispatchEvent</a>(<var>event</var>)</code>
10231046
<dd><a>Dispatches</a> a synthetic event <var>event</var> to <var>target</var> and returns
@@ -1026,24 +1049,40 @@ seen from the definition above, an <a>event listener</a> is a more broad concept
10261049
</dl>
10271050

10281051
<p>The
1029-
<dfn method for=EventTarget><code>addEventListener(<var>type</var>, <var>callback</var>, <var>capture</var>)</code></dfn>
1052+
<dfn method for=EventTarget><code>addEventListener(<var>type</var>, <var>callback</var>, <var>options</var>)</code></dfn>
10301053
method, when invoked, must run these steps:
10311054

10321055
<ol>
10331056
<li><p>If <var>callback</var> is null, terminate these steps.
10341057

1058+
<li>If <var>options</var> is of type boolean, let <var>capture</var> be
1059+
<var>options</var> and let <var>passive</var> be false. Otherwise let
1060+
<var>capture</var> and <var>passive</var> be the corresponding values in the
1061+
<var>options</var> {{EventListenerOptions}} dictionary.
1062+
10351063
<li><p>Append an <a>event listener</a> to the associated list of <a>event listeners</a> with
1036-
<b>type</b> set to <var>type</var>, <b>callback</b> set to <var>callback</var>, and <b>capture</b>
1037-
set to <var>capture</var>, unless there already is an <a>event listener</a> in that list with the
1038-
same <b>type</b>, <b>callback</b>, and <b>capture</b>.
1064+
<b>type</b> set to <var>type</var>, <b>callback</b> set to <var>callback</var>, <b>capture</b>
1065+
set to <var>capture</var>, and <b>passive</b> set to <var>passive</var> unless there
1066+
already is an <a>event listener</a> in that list with the same <b>type</b>,
1067+
<b>callback</b>, <b>capture</b>, and <b>passive</b>.
10391068
</ol>
10401069

10411070
<p>The
1042-
<dfn method for=EventTarget><code>removeEventListener(<var>type</var>, <var>callback</var>, <var>capture</var>)</code></dfn>
1043-
method, when invoked, must, if there is an <a>event listener</a> in the associated list of
1044-
<a>event listeners</a> whose <b>type</b> is <var>name</var>, <b>callback</b> is <var>callback</var>,
1045-
and <b>capture</b> is <var>capture</var>, set that <a>event listener</a>'s <b>removed flag</b> and
1046-
remove it from the associated list of <a>event listeners</a>.
1071+
<dfn method for=EventTarget><code>removeEventListener(<var>type</var>, <var>callback</var>, <var>options</var>)</code></dfn>
1072+
method, when invoked, must, run these steps
1073+
1074+
<ol>
1075+
<li>If <var>options</var> is of type boolean, let <var>capture</var> be
1076+
<var>options</var> and let <var>passive</var> be false. Otherwise let
1077+
<var>capture</var> and <var>passive</var> be the corresponding values in the
1078+
<var>options</var> {{EventListenerOptions}} dictionary.
1079+
1080+
<li>If there is an <a>event listener</a> in the associated list of
1081+
<a>event listeners</a> whose <b>type</b> is <var>name</var>, <b>callback</b> is <var>callback</var>,
1082+
<b>capture</b> is <var>capture</var>, and <b>passive</b> is <var>passive</var> then
1083+
set that <a>event listener</a>'s <b>removed flag</b> and remove it from the
1084+
associated list of <a>event listeners</a>.
1085+
</ol>
10471086

10481087
<p>The <dfn method for=EventTarget><code>dispatchEvent(<var>event</var>)</code></dfn> method, when
10491088
invoked, must run these steps:
@@ -1057,6 +1096,25 @@ invoked, must run these steps:
10571096
<li><p><a>Dispatch</a> the <var>event</var> and return the value that returns.
10581097
</ol>
10591098

1099+
<h3 id=observing-event-listeners>Observing event listeners</h3>
1100+
In general, developers do not expect the presence of an <a>event listener</a> to be
1101+
observable. The impact of an <a>event listener</a> is determined by its <b>callback</b>.
1102+
That is, a developer adding a no-op <a>event listener</a> would not expect it to have
1103+
any side effects.
1104+
1105+
Unfortunately, some event APIs have been designed such that implementing them
1106+
efficiently requires observing <a>event listeners</a>. For example, sensor APIs which
1107+
enable an underlying device sensor, and touch APIs which can be used to block
1108+
asynchronous scrolling. In some cases this problem can be mitigated by specifying
1109+
the event to be {{Event/cancelable}} only when there is at least one
1110+
non-{{EventListenerOptions/passive}} listener. For example, non-{{EventListenerOptions/passive}}
1111+
{{TouchEvent}} listeners must block scrolling, but if all listeners are {{EventListenerOptions/passive}} then
1112+
scrolling can be allowed to start <a>in parallel</a> by making the {{TouchEvent}}
1113+
uncancelable (so that calls to {{Event/preventDefault()}} are ignored).
1114+
Ideally, any new event types are defined such that they don't need this
1115+
property (use <a href="https://lists.w3.org/Archives/Public/public-script-coord/">[email protected]</a>
1116+
for discussion).
1117+
10601118

10611119
<h3 id=dispatching-events>Dispatching events</h3>
10621120

@@ -1138,9 +1196,15 @@ invoked, must run these steps:
11381196
<var>listener</var>'s <b>capture</b> is true, terminate these substeps (and run them for the next
11391197
<a>event listener</a>).
11401198

1199+
<li>If <var>listener</var>'s <b>passive</b> is true, set <var>event</var>'s <a>passive flag</a>.
1200+
11411201
<li><p>Call <var>listener</var>'s <b>callback</b>'s {{EventListener/handleEvent()}}, with
11421202
<var>event</var> as argument and <var>event</var>'s {{Event/currentTarget}} attribute value as
1143-
<a>callback this value</a>. If this throws any exception, <a>report the exception</a>.
1203+
<a>callback this value</a>.
1204+
1205+
<li>Clear <var>event</var>'s <a>passive flag</a>.
1206+
1207+
<li>If the call to {{EventListener/handleEvent()}} threw any exception, <a>report the exception</a>.
11441208
</ol>
11451209
</ol>
11461210

@@ -9097,6 +9161,7 @@ Peter Sharpe,
90979161
Philip Jägenstedt,
90989162
Philippe Le Hégaret,
90999163
Rafael Weinstein,
9164+
Rick Byers,
91009165
Rick Waldron,
91019166
Robbert Broersma,
91029167
Robin Berjon,

0 commit comments

Comments
 (0)