From d2009c27555001ba421ab07c0a3d2b1fc1cda5ff Mon Sep 17 00:00:00 2001 From: Keith Cirkel Date: Thu, 5 Oct 2023 09:17:32 +0100 Subject: [PATCH] Add commandfor & command attributes to HTMLButtonElement This adds the commandfor & command attributes and a "command" event using the CommandEvent interface. Button activation checks if the button has a "commandfor" target and if so performs invoker command behaviour depending on command and the target element. This introduces 1 new IDLs: - CommandEvent: A new event that has `action` and `source`. And extends the HTMLButtonElement IDL to introduce `command` and `commandfor` Things NOT covered in this commit that are included in the proposal (https://github.com/whatwg/html/issues/9625): - Default per element behaviours for anything beyond popover & dialog, this will be dealt with in subsequent individual commits. - `interestaction` and `interesttarget`. --- source | 450 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 419 insertions(+), 31 deletions(-) diff --git a/source b/source index 0d9624488bd..f6fae2d61f0 100644 --- a/source +++ b/source @@ -13310,6 +13310,7 @@ https://software.hixie.ch/utilities/js/live-dom-viewer/?%3C%21DOCTYPE%20HTML%3E%
  • onchange
  • onclick
  • onclose
  • +
  • oncommand
  • oncontextlost
  • oncontextmenu
  • oncontextrestored
  • @@ -53352,6 +53353,8 @@ You cannot submit this form when the field is incorrect. specified.
    Content attributes:
    Global attributes
    +
    command
    +
    commandfor
    disabled
    form
    formaction
    @@ -53374,6 +53377,8 @@ You cannot submit this form when the field is incorrect. interface HTMLButtonElement : HTMLElement { [HTMLConstructor] constructor(); + [CEReactions] attribute DOMString? command; + [CEReactions] attribute Element? commandForElement; [CEReactions] attribute boolean disabled; readonly attribute HTMLFormElement? form; [CEReactions] attribute USVString formAction; @@ -53438,13 +53443,62 @@ interface HTMLButtonElement : HTMLElement { data-x="attr-button-type-submit-state">Submit Button state, the element is specifically a submit button.

    -
    -

    Constraint validation: If the type attribute is in the Reset Button state or the Button state, the element is barred from constraint validation.

    +

    If specified, the commandfor + attribute value must be the ID of an element in + the same tree as the button with the commandfor attribute.

    + +

    The command attribute + is an enumerated attribute with the following keywords and states:

    + + + + + + + + + + +
    Keyword + State + Brief description +
    toggle-popover + Toggle popover + Shows or hides the targeted popover element. +
    show-popover + Show popover + Shows the targeted popover element. +
    hide-popover + Hide popover + Hides the targeted popover element. +
    close + Close + Closes the targeted dialog element. +
    show-modal + Show modal + Opens the targeted dialog element as modal. +
    + +

    The attribute's missing value default and invalid value default are both the unknown state.

    + +

    A valid custom command is a string whose first two code points are the U+002D + HYPHEN-MINUS character.

    + +
    +

    A button element element's activation behavior given event is:

    @@ -53455,6 +53509,11 @@ interface HTMLButtonElement : HTMLElement {
  • If element's node document is not fully active, then return.

  • +
  • If element has a form owner and element's commandfor or command attributes are present, + and the element's type attribute is not in + the button state, then return.

  • +
  • If element has a form owner then switch on element's type attribute's state, then:

    @@ -53475,10 +53534,145 @@ interface HTMLButtonElement : HTMLElement {
  • -
  • Run the popover target attribute activation behavior given +

  • Let invokee be the result of running element's get the commandfor-associated + element.

  • + +
  • +

    If invokee is not null, then:

    + +
      +
    1. If element has a form owner and element's type attribute is not in the button state, then return.

    2. + +
    3. Let command be element's + command attribute.

    4. + +
    5. Let isCustom be true if the command attribute is in the + unknown state, and command's + value is a valid custom command, and false otherwise.

    6. + +
    7. If the command attribute is in the + unknown state and isCustom is false, + then return.

    8. + +
    9. Let isPopover be true if invokee's + popover attribute is not in the + no popover state, and false otherwise.

    10. + +
    11. +

      If isPopover is false and isCustom is false, then:

      + +
        +
      1. Assert: invokee's + namespace is the HTML namespace.

      2. + +
      3. If this standard does not define is valid invoker command steps for + invokee's local name, then + return.

        + +
      4. Otherwise, if the result of running invokee's corresponding is valid + invoker command steps given command is not true, then return.

      5. +
      +
    12. + +
    13. Let continue be the result of firing an + event named command at invokee, using + CommandEvent, with its command attribute + initialized to command's value, its source attribute initialized to element, and its cancelable and composed attributes initialized to true.

    14. + +
    15. If continue is false, then return.

    16. + +
    17. If isCustom is true, then return.

    18. + +
    19. Let shouldShowPopover be false.

    20. + +
    21. Let shouldHidePopover be false.

    22. + +
    23. +

      If command is in the + hidepopover state, then:

      + +
        +
      1. If invokee's popover visibility state is + showing, then set shouldHidePopover to + true.

      2. + +
      3. Otherwise return.

      4. +
      +
    24. + +
    25. +

      If command is in the + showpopover state, then:

      + +
        +
      1. If invokee's popover visibility state is + hidden, then set shouldShowPopover to + true. + +

      2. Otherwise return.

      3. +
      +
    26. + +
    27. +

      If shouldShowPopover is true and the result of + running check popover validity given invokee, false, false, and null + is true, then:

      + +
        +
      1. Assert: shouldHidePopover is false.

      2. + +
      3. Assert: isPopover is true.

      4. + +
      5. Run the show popover given invokee, false, and + element.

      6. +
      +
    28. + +
    29. +

      Otherwise, if shouldHidePopover is true and the result of + running check popover validity given invokee, true, false, and null + is true, then:

      + +
        +
      1. Assert: shouldShowPopover is false.

      2. + +
      3. Assert: isPopover is true.

      4. + +
      5. Run the hide popover algorithm given invokee, true, true, and + false.

      6. +
      +
    30. + +
    31. +

      Otherwise, if this standard defines invoker command steps for + invokee's local name, then:

      + +
        +
      1. Assert: shouldShowPopover is false and + shouldHidePopover is false.

      2. + +
      3. Run the corresponding invoker command steps given invokee, + element and command.

      4. +
      +
    32. +
    +
  • + +
  • Otherwise, run the popover target attribute activation behavior given element.

  • + +

    An HTML element can have specific is valid invoker + command steps and invoker command steps defined for the element's + local name.

    +

    The form attribute is used to explicitly associate the @@ -53512,10 +53706,15 @@ interface HTMLButtonElement : HTMLElement {

    A button (and its value) is only included in the form submission if the button itself was used to initiate the form submission.

    -
    -
    +

    The commandForElement IDL attribute must + reflect the content attribute of the same name.

    + +

    The command + IDL attribute must reflect the content attribute of the same name.

    +

    The value IDL attribute must reflect the content attribute of the same name.

    @@ -53547,8 +53746,52 @@ interface HTMLButtonElement : HTMLElement {
    +
    +

    The following buttons use commandfor to open and close a popover box when activated:

    + +
    <button type=button
    +        commandfor="the-popover"
    +        command="toggle-popover">
    + Show menu
    +</button>
    +<div popover
    +     id="the-popover">
    + <button commandfor="the-popover"
    +         command="hide-popover">
    +  Hide menu
    + </button>
    +</div>
    +    
    +
    +
    +

    The following buttons use commandfor with a custom command on an element, demonstrating how + one could utilise custom commands for unspecified behavior:

    +
    <button type=button
    +        commandfor="the-image"
    +        command="--rotate-left">
    + Rotate Left
    +</button>
    +<button type=button
    +        commandfor="the-image"
    +        command="--rotate-right">
    + Rotate Right
    +</button>
    +<img id="the-image"
    +     src="photo.jpg">
    +<script>
    +  const image = document.getElementById("the-image");
    +  image.addEventListener("command", (event) => {
    +   if ( event.command == "--rotate-left" ) {
    +    event.target.style.rotate = "-90deg"
    +   } else if ( event.command == "--rotate-right" ) {
    +    event.target.style.rotate = "90deg"
    +   }
    +  });
    +</script>
    +    
    +

    The select element

    @@ -61823,19 +62066,26 @@ interface HTMLDialogElement : HTMLElement { data-x="dom-dialog-showModal">showModal() method steps are:

      -
    1. If this has an open attribute and the - is modal flag of this is true, then return.

    2. +
    3. Show this as modal.

    4. +
    -
  • If this has an open attribute, then - throw an "InvalidStateError" DOMException.

  • +

    When a dialog element subject is to be + shown as modal, run these steps:

    -
  • If this's node document is not fully active, then +

      +
    1. If subject has an open attribute and the + is modal flag of subject is true, then return.

    2. + +
    3. If subject has an open attribute, then throw an "InvalidStateError" DOMException.

    4. -
    5. If this is not connected, then throw an +

    6. If subject's node document is not fully active, + then throw an "InvalidStateError" DOMException.

    7. + +
    8. If subject is not connected, then throw an "InvalidStateError" DOMException.

    9. -
    10. If this is in the popover showing +

    11. If subject is in the popover showing state, then throw an "InvalidStateError" DOMException.

    12. @@ -61844,28 +62094,28 @@ interface HTMLDialogElement : HTMLElement { data-x="dom-Event-cancelable">cancelable attribute initialized to true, the oldState attribute initialized to "closed", and the newState - attribute initialized to "open" at this is false, then + attribute initialized to "open" at subject is false, then return.

      -
    13. If this has an open attribute, +

    14. If subject has an open attribute, then return.

    15. -
    16. If this is not connected, then return.

    17. +
    18. If subject is not connected, then return.

    19. -
    20. If this is in the popover showing +

    21. If subject is in the popover showing state, then return.

    22. Queue a dialog toggle event task given subject, "closed", and "open".

    23. -
    24. Add an open attribute to this, whose +

    25. Add an open attribute to subject, whose value is the empty string.

    26. -
    27. Set the is modal flag of this to true.

    28. +
    29. Set the is modal flag of subject to true.

    30. -

      Let this's node document be blocked by the modal dialog this.

      +

      Let subject's node document be blocked by the modal dialog subject.

      This will cause the focused area of the document to become inert (unless that currently focused area is a @@ -61875,41 +62125,41 @@ interface HTMLDialogElement : HTMLElement { focus.

    31. -
    32. If this's node document's top layer does not - already contain this, then add an element - to the top layer given this.

    33. +
    34. If subject's node document's top layer does not + already contain subject, then add an element + to the top layer given subject.

    35. -

      Set this's close watcher to the +

      Set subject's close watcher to the result of establishing a close watcher given - this's relevant global object, with:

      + subject's relevant global object, with:

      • cancelAction given canPreventClose being to return the result of firing an event named cancel at this, with the cancel at subject, with the cancelable attribute initialized to canPreventClose.

      • closeAction being to close the - dialog given this and null.

      • + dialog given subject and null.

    36. -
    37. Set this's previously focused element to the +

    38. Set subject's previously focused element to the focused element.

    39. Let hideUntil be the result of running topmost popover ancestor - given this, null, and false.

    40. + given subject, null, and false.

      -
    41. If hideUntil is null, then set hideUntil to this's +

    42. If hideUntil is null, then set hideUntil to subject's node document.

    43. Run hide all popovers until given hideUntil, false, and true.

    44. -
    45. Run the dialog focusing steps given this.

    46. +
    47. Run the dialog focusing steps given subject.

    The dialog focusing steps, given a dialog element subject, @@ -61951,6 +62201,76 @@ interface HTMLDialogElement : HTMLElement {

  • Set topDocument's autofocus processed flag to true.

  • +

    The is valid invoker command steps, given a dialog element + subject and command, are:

    + +
      +
    1. If command is in the close + state, then return true.

    2. + +
    3. If command is in the show + modal state, then return true.

    4. + +
    5. Return false.

    6. +
    + +

    The invoker command steps, given a dialog element invokee, + invoker, and command, are:

    + +
      +
    1. If invokee is in the popover showing + state, then return.

    2. + +
    3. +

      If command is in the + close state, then:

      + +
        +
      1. If invokee has an open attribute, + then:

        + +
          +
        1. Let value be invoker's value +

        2. + +
        3. Close the dialog invokee with value.

        4. +
        +
      +
    4. + +
    5. +

      If command is in the show modal + state, then:

      + +
        +
      1. If invokee does not have an open + attribute, then show modal dialog invokee.

      2. +
      +
    6. +
    + +
    +

    The following buttons use commandfor to open and close a "confirm" dialog as modal when activated:

    + +
    <button type=button
    +        commandfor="the-dialog"
    +        command="show-modal">
    + Delete
    +</button>
    +<dialog id="the-dialog">
    + <form action="/delete" method="POST">
    +  <button type="submit">
    +   Delete
    +  </button>
    +  <button commandfor="the-dialog"
    +          command="close">
    +   Cancel
    +  </button>
    + </form>
    +</dialog>
    +    
    +
    +

    The dialog HTML element removing steps, given removedNode and oldParent, are:

    @@ -79501,6 +79821,43 @@ dictionary ToggleEventInit : EventInit { the oldState attribute. +

    The CommandEvent interface

    + +
    [Exposed=Window]
    +interface CommandEvent : Event {
    +  constructor(DOMString type, optional CommandEventInit eventInitDict = {});
    +  readonly attribute Element? source;
    +  readonly attribute DOMString action;
    +};
    +
    +dictionary CommandEventInit : EventInit {
    +  Element? source = null;
    +  DOMString action = "auto";
    +};
    + +
    +
    event.command
    + +
    +

    Returns what action the element can take.

    +
    + +
    event.source
    + +
    +

    Returns the Element that was interacted with in order to cause this event.

    +
    +
    + +

    The command + attribute must return the value it as initialized to.

    + +

    The source + getter steps are to return the result of retargeting source against this's currentTarget.

    Focus

    @@ -112922,6 +113279,7 @@ typedef OnBeforeUnloadEventHandlerNonNull? OnBeforeUnl onchange change onclick click onclose close + oncommand command oncontextlost contextlost oncontextmenu contextmenu oncontextrestored contextrestored @@ -113094,6 +113452,7 @@ typedef OnBeforeUnloadEventHandlerNonNull? OnBeforeUnl attribute EventHandler onchange; attribute EventHandler onclick; attribute EventHandler onclose; + attribute EventHandler oncommand; attribute EventHandler oncontextlost; attribute EventHandler oncontextmenu; attribute EventHandler oncontextrestored; @@ -142011,6 +142370,22 @@ interface External { script Integrity metadata used in Subresource Integrity checks SRI Text + + command + button + Indicates to the targeted element which action to take. + "toggle-popover"; + "show-popover"; + "hide-popover"; + "close"; + "show-modal"; + a valid custom command; + the empty string + + commandfor + button + Targets another element to be invoked. + ID* is HTML elements @@ -142728,6 +143103,12 @@ interface External { close event handler Event handler content attribute + + oncommand + HTML elements + command event handler + Event handler content attribute + oncontextlost HTML elements @@ -143735,6 +144116,13 @@ INSERT INTERFACES HERE CloseWatcher, dialog elements, MessagePort Fired at CloseWatcher objects or dialog elements when they are closed via a close request or via web developer code, or at MessagePort objects when disentangled + + command + CommandEvent + Elements + Fired at elements when they handle a user invocation, via a commandfor attribute. + connect MessageEvent