DEV Community

Greg, The JavaScript Whisperer
Greg, The JavaScript Whisperer

Posted on • Edited on

Invoker Commands API

Invoker Commands API
The Invoker Commands API provides a way to declaratively assign behaviours to buttons, allowing control of interactive elements when the button is enacted (clicked or invoked via a keypress, such as the spacebar or return key).
src

What does that mean though? Basically we have a possibly growing amount of declarative HTML that can add interactivity, without javascript 🤯

If you're a old and kind of clever like me this might trigger happy memories of the <marquee>Do you believe in love after love</marquee>
element

HTML attributes

commandfor
Turns a element into a button, controlling the given interactive element; takes the ID of the element to control as its value.

command
Specifies the action to be performed on an element being controlled by a control , specified via the commandfor attribute.

src

and an example from MDN

<button commandfor="mydialog" command="show-modal">Show modal dialog</button>
<dialog id="mydialog">
  <button commandfor="mydialog" command="close">Close</button>
  Dialog Content
</dialog>
Enter fullscreen mode Exit fullscreen mode

So what's interesting about the above example, at least to me is it actually runs with javascript disabled!?! So only built in the browser commands will work, but I think this is cool. Really primitive elements that add a better UX to the user can now run in environments without javascript. Also this pattern has better Accessibility out of the box.

You can also write your own custom js events and handlers...

Creating custom commands

<button commandfor="my-img" command="--rotate-left">Rotate left</button>
<button commandfor="my-img" command="--rotate-right">Rotate right</button>
<img id="my-img" src="photo.jpg" alt="[add appropriate alt text here]" />
js
Copy to Clipboard
const myImg = document.getElementById("my-img");

myImg.addEventListener("command", (event) => {
  if (event.command == "--rotate-left") {
    myImg.style.rotate = "-90deg";
  } else if (event.command == "--rotate-right") {
    myImg.style.rotate = "90deg";
  }
});
Enter fullscreen mode Exit fullscreen mode

Thought I'd fork this article

DEMO


<button commandfor="modalexample" command="show-modal">
  Open modal dialog
</button>
<dialog class="modal-content" id="modalexample" class="">
  <h2>Welcome to the Modal!</h2>
  <p>This modal window is created using only HTML and CSS!</p>

  <button commandfor="modalexample" command="close">Close Modal</button>
</dialog>

Enter fullscreen mode Exit fullscreen mode
/* Modal container - hidden by default */
.modal {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.6);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;

  /* Center the modal content */
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
}

/* When the modal is targeted via URL hash, make it visible */
.modal:target {
  opacity: 1;
  pointer-events: auto;
}

/* Modal content box */
.modal-content {
  background-color: white;
  padding: 2rem;
  border-radius: 6px;
  width: 90%;
  max-width: 500px;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: 0 5px 30px rgba(0, 0, 0, 0.2);
}

/* Backdrop close link - covers the entire screen */
.modal-backdrop {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  cursor: default;
  z-index: -1; /* Place behind modal content */
}

dialog:modal {
  transition: all 1s ease;
  overlay: auto !important;
}
/* Keyframes for dialog and popover elements */
@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
@keyframes fadeOut {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

/* Keyframes for the backdrop pseudo-element */
@keyframes backdropFadeIn {
  from {
    background: hsl(0 0% 0% / 0%);
  }
  to {
    background: hsl(0 0% 0% / 50%);
  }
}
@keyframes backdropFadeOut {
  from {
    background: hsl(0 0% 0% / 50%);
  }
  to {
    background: hsl(0 0% 0% / 0%);
  }
}

dialog::backdrop {
  transition: all 1s ease;
  opacity: 1;
  transform: scaleX(1);
}

[dialog] {
  font-size: 1.2rem;
  padding: 10px;

  /* Final state of the exit animation */
  opacity: 0;
  transform: scaleX(0);

  transition:
    opacity 0.7s,
    transform 0.7s,
    overlay 0.7s allow-discrete,
    display 0.7s allow-discrete;
  /* Equivalent to
    transition: all 0.7s allow-discrete; */
}

/* Needs to be included after the previous [popover]:popover-open
     rule to take effect, as the specificity is the same */
@starting-style {
  [popover]:popover-open {
    opacity: 0;
    transform: scaleX(0);
  }
}

/* Transition for the popover's backdrop */

[popover]::backdrop {
  background-color: rgb(0 0 0 / 0%);
  transition:
    display 0.7s allow-discrete,
    overlay 0.7s allow-discrete,
    background-color 0.7s;
  /* Equivalent to
    transition: all 0.7s allow-discrete; */
}

[popover]:popover-open::backdrop {
  background-color: rgb(0 0 0 / 25%);
}

/* Nesting selectors (&) cannot represent pseudo-elements, so this 
     starting-style rule cannot be nested. */

@starting-style {
  [popover]:popover-open::backdrop {
    background-color: rgb(0 0 0 / 0%);
  }
}

#modalexample {
  animation: fadeOut 0.5s forwards;
  &::backdrop {
    animation: backdropFadeOut 0.5s forwards;
  }
  &[open] {
    animation: fadeIn 0.5s forwards;
    &::backdrop {
      animation: backdropFadeIn 0.5s forwards;
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

https://github.com/jswhisperer/re-modals

I'm not 100% overall on how I feel about that though, maybe it helps lower level with creating interactive elements... Maybe it feels like too little too late, time will tell.

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (1)

Collapse
 
jswhisperer profile image
Greg, The JavaScript Whisperer

css animations sourced from this article