From 2bdabb15de8112d9783cf393a3d6c37911149f77 Mon Sep 17 00:00:00 2001 From: Anne van Kesteren Date: Wed, 14 Mar 2018 09:40:36 +0100 Subject: [PATCH] Editorial: add "add an event listener" hook This hook ensures that any special casing in addEventListener() is shared with event handlers. This commit also makes numerous editorial improvements that were long overdue around dictionary members. The corresponding change to the HTML Standard, which defines event handlers, is tracked by https://github.com/whatwg/html/pull/3561. This helps with #365 and https://github.com/w3c/ServiceWorker/issues/1004. (Tests will be added as part of #365 eventually.) --- dom.bs | 323 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 159 insertions(+), 164 deletions(-) diff --git a/dom.bs b/dom.bs index d86304f10..59d7a5a62 100644 --- a/dom.bs +++ b/dom.bs @@ -328,7 +328,7 @@ method, passing the same arguments. {{Event}} interface (or a derived interface). In the example above ev is the event. It is passed as argument to -event listener's callback +event listener's callback (typically a JavaScript Function as shown above). Event listeners key off the event's @@ -384,7 +384,7 @@ When an event is ancestors too. First all object's ancestor event listeners whose -capture variable is set to true are invoked, in +capture variable is set to true are invoked, in tree order. Second, object's own event listeners are invoked. And finally, and only if event's @@ -511,7 +511,7 @@ algorithm below.
Returns the object to which event is dispatched.
event . {{Event/currentTarget}} -
Returns the object whose event listener's callback is currently being +
Returns the object whose event listener's callback is currently being invoked.
event . {{Event/composedPath()}} @@ -910,27 +910,27 @@ dictionary AddEventListenerOptions : EventListenerOptions { }; -

The {{EventTarget}} object represents the target to which an event is dispatched +

An {{EventTarget}} object represents a target to which an event can be dispatched when something has occurred. -

Each {{EventTarget}} object has an associated list of event listeners. +

Each {{EventTarget}} object has an associated event listener list (a +list of zero or more event listeners). It is initially the empty list. +

An event listener can be used to observe a specific -event. - -

An event listener consists of these fields:

+event and consists of: -

Although callback is an {{EventListener}}, as can be seen from the -fields above, an event listener is a broader concept. +

Although callback is an {{EventListener}} +object, an event listener is a broader concept as can be seen above.

Each {{EventTarget}} object also has an associated get the parent algorithm, which takes an event event, and returns an {{EventTarget}} object. Unless @@ -958,48 +958,48 @@ are not to be used for anything else. [[!HTML]]

target = new EventTarget(); -
- Creates a new {{EventTarget}} object, which can be used by developers to dispatch and listen for - events. +

Creates a new {{EventTarget}} object, which can be used by developers to dispatch and + listen for events.

target . addEventListener(type, callback [, options])
- Appends an event listener for events whose {{Event/type}} attribute value - is type. The callback argument sets the callback that will - be invoked when the event is dispatched. - - The options argument sets listener-specific options. For compatibility this can be just - a boolean, in which case the method behaves exactly as if the value was specified as - options' capture member. - - When set to true, options' capture member prevents callback from - being invoked when the event's {{Event/eventPhase}} attribute value is - {{Event/BUBBLING_PHASE}}. When false (or not present), callback will not be invoked when - event's {{Event/eventPhase}} attribute value is {{Event/CAPTURING_PHASE}}. Either way, - callback will be invoked if event's {{Event/eventPhase}} attribute value is - {{Event/AT_TARGET}}. - - When set to true, options' passive member indicates that the - callback will not cancel the event by invoking {{Event/preventDefault()}}. This is used to - enable performance optimizations described in [[#observing-event-listeners]]. - - When set to true, options's once member indicates that the callback - will only be invoked once after which the event listener will be removed. - - The event listener is appended to target's list of event listeners and is - not appended if it is a duplicate, i.e., having the same type, callback, and - capture values. +

Appends an event listener for events whose {{Event/type}} attribute value is + type. The callback argument sets the callback + that will be invoked when the event is dispatched. + +

The options argument sets listener-specific options. For compatibility this can be a + boolean, in which case the method behaves exactly as if the value was specified as + options's {{EventListenerOptions/capture}}. + +

When set to true, options's {{EventListenerOptions/capture}} prevents + callback from being invoked when the event's + {{Event/eventPhase}} attribute value is {{Event/BUBBLING_PHASE}}. When false (or not present), + callback will not be invoked when event's {{Event/eventPhase}} + attribute value is {{Event/CAPTURING_PHASE}}. Either way, callback + will be invoked if event's {{Event/eventPhase}} attribute value is {{Event/AT_TARGET}}. + +

When set to true, options's {{AddEventListenerOptions/passive}} indicates that the + callback will not cancel the event by invoking + {{Event/preventDefault()}}. This is used to enable performance optimizations described in + [[#observing-event-listeners]]. + +

When set to true, options's {{AddEventListenerOptions/once}} indicates that the + callback will only be invoked once after which the event listener will + be removed. + +

The event listener is appended to target's + event listener list and is not appended if it has the same + type, callback, and + capture.

target . removeEventListener(type, callback [, options]) -
Remove the event listener - in target's list of - event listeners with the same - type, callback, and +

Removes the event listener in target's + event listener list with the same type, callback, and options.

target . dispatchEvent(event) -
Dispatches a synthetic event event to target and returns - true if either event's {{Event/cancelable}} attribute value is false or its +

Dispatches a synthetic event event to target and returns true + if either event's {{Event/cancelable}} attribute value is false or its {{Event/preventDefault()}} method was not invoked, and false otherwise.

@@ -1038,13 +1038,12 @@ must return a new {{EventTarget}}. if this would be useful for your programs. For now, all author-created {{EventTarget}}s do not participate in a tree structure.

-

The -addEventListener(type, callback, options) -method, when invoked, must run these steps: +

To add an event listener given an {{EventTarget}} object eventTarget +and an event listener listener, run these steps:

  1. -

    If context object's relevant global object is a {{ServiceWorkerGlobalScope}} +

    If eventTarget's relevant global object is a {{ServiceWorkerGlobalScope}} object and its associated service worker's script resource's has ever been evaluated flag is set, then throw a TypeError. @@ -1054,34 +1053,53 @@ method, when invoked, must run these steps: to avoid non-deterministic changes to the event listeners, invocation of the method is allowed only during the very first evaluation of the service worker script. -

  2. If callback is null, then return. +

  3. If listener's callback is null, then return. +

  4. If eventTarget's event listener list does not contain an + event listener whose type is listener's + type, callback is listener's + callback, and capture is + listener's capture, then append + listener to eventTarget's event listener list. +

+ +

The add an event listener concept exists to ensure event handlers use +the same code path. [[HTML]] + +

The +addEventListener(type, callback, options) +method, when invoked, must run these steps: + +

  1. Let capture, passive, and once be the result of flattening more options. -

  2. If context object's associated list of event listener does not contain an - event listener whose type is type, callback is callback, - and capture is capture, then append a new event listener to it, whose - type is type, callback is callback, capture is - capture, passive is passive, and once is once. +

  3. Add an event listener with the context object and an event listener + whose type is type, callback is + callback, capture is capture, + passive is passive, and once is + once.

The removeEventListener(type, callback, options) -method, when invoked, must, run these steps +method, when invoked, must run these steps:

    -
  1. If context object's relevant global object is a {{ServiceWorkerGlobalScope}} - object and its associated service worker's script resource's +

  2. If the context object's relevant global object is a + {{ServiceWorkerGlobalScope}} object and its associated service worker's + script resource's has ever been evaluated flag is set, then throw a TypeError. [[!SERVICE-WORKERS]]

  3. Let capture be the result of flattening options. -

  4. If there is an event listener in the associated list of event listeners whose - type is type, callback is callback, and capture is - capture, then set that event listener's removed to true and remove it from - the associated list of event listeners. +

  5. If the context object's event listener list + contains an event listener whose type is + type, callback is callback, and + capture is capture, then set that event listener's + removed to true and remove it from the + context object's event listener list.

The dispatchEvent(event) method, when @@ -1100,7 +1118,7 @@ invoked, must run these steps:

Observing event listeners

In general, developers do not expect the presence of an event listener to be observable. -The impact of an event listener is determined by its callback. That is, a developer +The impact of an event listener is determined by its callback. That is, a developer adding a no-op event listener would not expect it to have any side effects.

Unfortunately, some event APIs have been designed such that implementing them efficiently @@ -1320,14 +1338,15 @@ for discussion).

  1. If event's stop propagation flag is set, then return. -

  2. Let listeners be a new list. +

  3. Let listeners be a new list.

  4. -

    For each event listener associated with object, append a - pointer to the event listener to listeners. +

    For each listener of object's + event listener list, append listener to + listeners.

    This avoids event listeners added after this point from being - run. Note that removal still has an effect due to the removed field. + run. Note that removal still has an effect due to the removed field.

  5. Initialize event's {{Event/currentTarget}} attribute to object. @@ -1371,32 +1390,33 @@ with event, listeners, and an optional

  6. Let found be false.

  7. -

    For each listener in listeners, whose removed is - false: +

    For each listener in listeners, whose + removed is false:

    1. If event's {{Event/type}} attribute value is not listener's - type, then continue. + type, then continue.

    2. Set found to true.

    3. If event's {{Event/eventPhase}} attribute value is {{Event/CAPTURING_PHASE}} - and listener's capture is false, then continue. + and listener's capture is false, then continue.

    4. If event's {{Event/eventPhase}} attribute value is {{Event/BUBBLING_PHASE}} and - listener's capture is true, then continue. + listener's capture is true, then continue. -

    5. If listener's once is true, then remove listener from - object's associated list of event listeners. +

    6. If listener's once is true, then + remove listener from object's + event listener list. -

    7. If listener's passive is true, then set event's - in passive listener flag. +

    8. If listener's passive is true, then set + event's in passive listener flag.

    9. -

      Call a user object's operation with listener's callback, - "handleEvent", a list of arguments consisting of event, and +

      Call a user object's operation with listener's + callback, "handleEvent", « event », and event's {{Event/currentTarget}} attribute value as the callback this value. If this throws an exception, then: @@ -3314,65 +3334,46 @@ of {{MutationObserver}} objects, and then return it. method, when invoked, must run these steps:

        -
      1. If either options' - {{MutationObserverInit/attributeOldValue}} or - {{MutationObserverInit/attributeFilter}} is present - and options' - {{MutationObserverInit/attributes}} is omitted, set - options' +
      2. If either options's {{MutationObserverInit/attributeOldValue}} or + {{MutationObserverInit/attributeFilter}} is present and options's + {{MutationObserverInit/attributes}} is omitted, then set options's {{MutationObserverInit/attributes}} to true. -

      3. If options' - {{MutationObserverInit/characterDataOldValue}} - is present and options' - {{MutationObserverInit/characterData}} is omitted, set - options' - {{MutationObserverInit/characterData}} to true. - -
      4. If none of options' - {{MutationObserverInit/childList}}, - {{MutationObserverInit/attributes}}, and - {{MutationObserverInit/characterData}} is true, - throw a TypeError. +
      5. If options's {{MutationObserverInit/characterDataOldValue}} is present and + options's {{MutationObserverInit/characterData}} is omitted, then set + options's {{MutationObserverInit/characterData}} to true. -

      6. If options' - {{MutationObserverInit/attributeOldValue}} is true - and options' - {{MutationObserverInit/attributes}} is false, +
      7. If none of options's {{MutationObserverInit/childList}}, + {{MutationObserverInit/attributes}}, and {{MutationObserverInit/characterData}} is true, then throw a TypeError. -

      8. If options' - {{MutationObserverInit/attributeFilter}} is present - and options' - {{MutationObserverInit/attributes}} is false, - throw a TypeError. +
      9. If options's {{MutationObserverInit/attributeOldValue}} is true and + options's {{MutationObserverInit/attributes}} is false, then throw a + TypeError. -

      10. If options' - {{MutationObserverInit/characterDataOldValue}} - is true and options' - {{MutationObserverInit/characterData}} is false, - throw a TypeError. +
      11. If options's {{MutationObserverInit/attributeFilter}} is present and + options's {{MutationObserverInit/attributes}} is false, then throw a + TypeError. + +

      12. If options's {{MutationObserverInit/characterDataOldValue}} is true and + options's {{MutationObserverInit/characterData}} is false, then throw a + TypeError.

      13. - For each registered observer registered in - target's list of - registered observers whose observer is - the context object: +

        For each registered observer registered in target's list of + registered observers whose observer is the context object:

          -
        1. Remove all - transient registered observers whose - source is registered. +
        2. Remove all transient registered observers whose source is + registered. -

        3. Replace registered's options with - options. +
        4. Replace registered's options with options.

        -
      14. Otherwise, add a new registered observer to - target's list of - registered observers with the - context object as the observer and options as the options, - and add target to context object's list of nodes on which it is registered. +
      15. Otherwise, add a new registered observer to target's list of + registered observers with the context object as the observer and + options as the options, and add target to context object's list + of nodes on which it is registered.

      The disconnect() method, when invoked, must @@ -3410,23 +3411,24 @@ previousSibling previousSibling, and nextSibling

      If none of the following are true

        -
      • node is not target and options' subtree is - false +
      • node is not target and options's + {{MutationObserverInit/subtree}} is false -
      • type is "attributes" and options' - attributes is not true +
      • type is "attributes" and options's + {{MutationObserverInit/attributes}} is not true -
      • type is "attributes", options' - attributeFilter is present, and options' attributeFilter - does not contain name or namespace is non-null +
      • type is "attributes", options's + {{MutationObserverInit/attributeFilter}} is present, and options's + {{MutationObserverInit/attributeFilter}} does not contain name or + namespace is non-null -
      • type is "characterData" and options' - characterData is not true +
      • type is "characterData" and options's + {{MutationObserverInit/characterData}} is not true -
      • type is "childList" and options' childList - is false +
      • type is "childList" and options's + {{MutationObserverInit/childList}} is false

      then: @@ -3436,9 +3438,10 @@ previousSibling previousSibling, and nextSibling interested observers, append registered observer's observer to interested observers. -

    10. If either type is "attributes" and options' - attributeOldValue is true, or type is "characterData" and - options' characterDataOldValue is true, set the paired string of +

    11. If either type is "attributes" and options's + {{MutationObserverInit/attributeOldValue}} is true, or type is + "characterData" and options's + {{MutationObserverInit/characterDataOldValue}} is true, set the paired string of registered observer's observer in interested observers to oldValue.

    @@ -4935,39 +4938,31 @@ for the context object.
    element = document . createElement(localName [, options])
    - Returns an element with localName as local name (if - document is an HTML document, localName gets lowercased). The +

    Returns an element with localName as local name + (if document is an HTML document, localName gets lowercased). The element's namespace is the HTML namespace when document is an HTML document or document's content type is "application/xhtml+xml", and null otherwise. - If localName does not match the - Name production an +

    If localName does not match the Name production an "{{InvalidCharacterError!!exception}}" {{DOMException}} will be thrown. - When supplied, options' is member can be used to create a +

    When supplied, options's {{ElementCreationOptions/is}} can be used to create a customized built-in element. -

    element = document . createElementNS(namespace, qualifiedName [, options])
    - Returns an element with - namespace - namespace. Its - namespace prefix will - be everything before ":" (U+003E) in - qualifiedName or null. Its - local name will be - everything after ":" (U+003E) in - qualifiedName or qualifiedName. - - If localName does not match the - Name production an +

    Returns an element with namespace namespace. Its + namespace prefix will be everything before ":" (U+003E) in + qualifiedName or null. Its local name will be everything after + ":" (U+003E) in qualifiedName or qualifiedName. + +

    If localName does not match the Name production an "{{InvalidCharacterError!!exception}}" {{DOMException}} will be thrown. - If one of the following conditions is true a - "{{NamespaceError!!exception}}" {{DOMException}} will be thrown: +

    If one of the following conditions is true a "{{NamespaceError!!exception}}" {{DOMException}} + will be thrown:

    • localName does not match the @@ -4987,7 +4982,7 @@ for the context object. is "xmlns".
    - When supplied, options' is member can be used to create a +

    When supplied, options's {{ElementCreationOptions/is}} can be used to create a customized built-in element.

    documentFragment = document . {{createDocumentFragment()}}