Marko

Events

Marko’s event API supports:

Note that you can’t mix event targets and event types: custom tags can only listen for custom events, and native tags can only listen for native events.

Listening to events

Both kinds of events are received with an on-* attribute and the attribute arguments syntax:

<input type="checkbox"
  on-change(event => console.info(`Checked? ${event.target.checked}`))
/>
input [
    type="checkbox"
    on-change(event => console.info(`Checked? ${event.target.checked}`))
]

The first argument for the attribute can be a function, or a string matching a method name on the component’s class declaration.

Function handler

If you provide a function as the first argument of the on-* attribute, the function is called whenever the event fires, like standard event listeners.

Below we use the static prefix to define a function, then use it as a click handler:

static function handleClick(event) {
  event.preventDefault();
  console.log("Clicked!");
}

<button on-click(handleClick)>
  Log click
</button>
static function handleClick(event) {
    event.preventDefault();
    console.log("Clicked!");
}

button on-click(handleClick) -- Log click

In the above example, any time the <button> is clicked the handleClick function is called.

You can also use an inline arrow function:

<button on-click(() => alert("Clicked! 🎉"))>
  Celebrate click
</button>
button on-click(() => alert("Clicked! 🎉")) -- Celebrate click

…or anything that evaluates to a function:

$ const handler = (
  input.dontBreakMyApp ?
    () => console.error("Clicked!") :
    () => { throw Error("Clicked!") }
);

<button on-click(handler)>
  Do not click
</button>
$ {
    const handler = input.dontBreakMyApp
        ? () => console.error("Clicked!")
        : () => {
              throw Error("Clicked!");
          };
}
button on-click(handler) -- Do not click

Method handler

When a string is the first argument, Marko calls a matching method on the component's class.

class {
  logChange(newTab) {
    console.log(`changed to: ${newTab}`);
  }
}

<my-tabs on-switch-tab("logChange")>

</my-tabs>
class {
    logChange(newTab) {
        console.log(`changed to: ${newTab}`);
    }
}

my-tabs on-switch-tab("logChange") --

When <my-tabs> emits the switch-tab event, it will call its logChange method.

Within the handler you can access the current component instance, read data, emit events, update state, etc.

Binding additional arguments

Arguments after the handler are prepended when the handler is called:

static function removeFriend(friendId, event) {
  event.preventDefault();
  window.myAPI.unfriend(friendId);
}

<for|friend| of=input.friends>
  <button on-click(removeFriend, friend.id)>
    Unfriend ${friend.name}
  </button>
</for>
static function removeFriend(friendId, event) {
    event.preventDefault();
    window.myAPI.unfriend(friendId);
}

for|friend| of=input.friends
    button on-click(removeFriend, friend.id) -- Unfriend ${friend.name}

Here we share the logic for removeFriend() with each friend in the friends array. When the <button> is clicked, the id of the removed friend is passed to the removeFriend(), handler followed by the DOM click event.

Emitting custom events

The recommended way for a custom tag to communicate with its parent is through custom events.

All components implement a Node.js-style event emitter to send events to parent components.

email-input.marko
class {
  handleChange(event) {
    if (event.target.validity.valid) {
      // Only emit email-changes if they are valid.
      this.emit("email-change", { email: event.target.value });
    }
  }
}

<input type="email" name=input.name on-change("handleChange")/>
class {
    handleChange(event) {
        if (event.target.validity.valid) {
            // Only emit email-changes if they are valid.
            this.emit("email-change", { email: event.target.value });
        }
    }
}

input type="email" name=input.name on-change("handleChange")

The above code listens to native change events from the <input> element, and then emits its own email-change event if the change was valid.

<form>
  <email-input name="email" on-email-change(...)/>
</form>
form
    email-input name="email" on-email-change(...)

Note: Events are not received as input; you cannot access input.onEmailChange. Instead, they set up subscriptions.

EDIT

Contributors

Helpful? You can thank these awesome people! You can also edit this doc if you see any issues or want to improve it.

Chat in Marko's Discord Server