2024-11-22 10:20:59 +01:00
|
|
|
|
import { T, t } from "./utils/index.js";
|
2024-05-22 21:43:49 +02:00
|
|
|
|
export const info= {
|
2024-11-22 10:20:59 +01:00
|
|
|
|
title: t`Events and Addons`,
|
|
|
|
|
description: t`Using not only events in UI declaratively.`,
|
2024-05-22 21:43:49 +02:00
|
|
|
|
};
|
2023-11-21 14:37:57 +01:00
|
|
|
|
|
2023-11-24 16:16:08 +01:00
|
|
|
|
import { el } from "deka-dom-el";
|
2024-05-22 21:43:49 +02:00
|
|
|
|
import { simplePage } from "./layout/simplePage.html.js";
|
2023-11-17 16:15:26 +01:00
|
|
|
|
import { example } from "./components/example.html.js";
|
2023-11-24 16:16:08 +01:00
|
|
|
|
import { h3 } from "./components/pageUtils.html.js";
|
2023-11-29 18:25:21 +01:00
|
|
|
|
import { mnemonic } from "./components/mnemonic/events-init.js";
|
2023-11-24 17:02:16 +01:00
|
|
|
|
import { code } from "./components/code.html.js";
|
2023-11-17 16:15:26 +01:00
|
|
|
|
/** @param {string} url */
|
|
|
|
|
const fileURL= url=> new URL(url, import.meta.url);
|
2024-11-22 10:20:59 +01:00
|
|
|
|
const references= {
|
|
|
|
|
/** element.addEventListener() */
|
|
|
|
|
mdn_listen: {
|
|
|
|
|
title: t`MDN documentation page for elemetn.addEventListener`,
|
|
|
|
|
href: "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener",
|
|
|
|
|
},
|
|
|
|
|
/** AbortSignal+element.addEventListener */
|
|
|
|
|
mdn_abortListener: {
|
|
|
|
|
title: t`MDN documentation page for using AbortSignal with element.addEventListener`,
|
|
|
|
|
href: "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#signal",
|
|
|
|
|
},
|
|
|
|
|
/** comparison listening options by WebReflection */
|
|
|
|
|
web_events: {
|
|
|
|
|
href: "https://gist.github.com/WebReflection/b404c36f46371e3b1173bf5492acc944",
|
|
|
|
|
},
|
|
|
|
|
/** Custom Element lifecycle callbacks */
|
|
|
|
|
mdn_customElement: {
|
2024-11-22 16:26:42 +01:00
|
|
|
|
href: "https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks" // editorconfig-checker-disable-line
|
2024-11-22 10:20:59 +01:00
|
|
|
|
},
|
|
|
|
|
/** MutationObserver */
|
|
|
|
|
mdn_mutation: {
|
|
|
|
|
href: "https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver",
|
|
|
|
|
},
|
|
|
|
|
/** Readding the element to the DOM fix by Vue */
|
|
|
|
|
vue_fix: {
|
|
|
|
|
title: t`Vue and Web Components, lifecycle implementation readding the element to the DOM`,
|
|
|
|
|
href: "https://vuejs.org/guide/extras/web-components.html#lifecycle",
|
|
|
|
|
}
|
|
|
|
|
};
|
2023-11-17 16:15:26 +01:00
|
|
|
|
/** @param {import("./types.d.ts").PageAttrs} attrs */
|
|
|
|
|
export function page({ pkg, info }){
|
|
|
|
|
const page_id= info.id;
|
2023-11-24 16:16:08 +01:00
|
|
|
|
return el(simplePage, { info, pkg }).append(
|
2024-11-22 10:20:59 +01:00
|
|
|
|
el("h2", t`Listenning to the native DOM events and other Addons`),
|
|
|
|
|
el("p").append(...T`
|
|
|
|
|
We quickly introduce helper to listening to the native DOM events. And library syntax/pattern so-called
|
|
|
|
|
${el("em", t`Addon`)} to incorporate not only this in UI templates declaratively.
|
|
|
|
|
`),
|
2024-11-22 16:26:42 +01:00
|
|
|
|
|
2023-11-24 17:02:16 +01:00
|
|
|
|
el(code, { src: fileURL("./components/examples/events/intro.js"), page_id }),
|
2024-11-22 16:26:42 +01:00
|
|
|
|
|
2024-11-22 10:20:59 +01:00
|
|
|
|
el(h3, t`Events and listenners`),
|
|
|
|
|
el("p").append(...T`
|
|
|
|
|
In JavaScript you can listen to the native DOM events of the given element by using
|
|
|
|
|
${el("a", references.mdn_listen).append( el("code", "element.addEventListener(type, listener, options)") )}.
|
|
|
|
|
The library provides an alternative (${el("code", "on")}) accepting the differen order of the arguments:
|
|
|
|
|
`),
|
2023-11-24 16:16:08 +01:00
|
|
|
|
el(example, { src: fileURL("./components/examples/events/compare.js"), page_id }),
|
2024-11-22 10:20:59 +01:00
|
|
|
|
el("p").append(...T`
|
|
|
|
|
…this is actually one of the two differences. The another one is that ${el("code", "on")} accepts only
|
|
|
|
|
object as the ${el("code", "options")} (but it is still optional).
|
|
|
|
|
`),
|
|
|
|
|
el("p", { className: "notice" }).append(...T`
|
|
|
|
|
The other difference is that there is ${el("strong", "no")} ${el("code", "off")} function. You can remove
|
|
|
|
|
listener declaratively using ${el("a", { textContent: "AbortSignal", ...references.mdn_abortListener })}:
|
|
|
|
|
`),
|
2023-11-24 16:16:08 +01:00
|
|
|
|
el(example, { src: fileURL("./components/examples/events/abortSignal.js"), page_id }),
|
|
|
|
|
el("div", { className: "notice" }).append(
|
2024-11-22 10:20:59 +01:00
|
|
|
|
el("p", t`So, there are (typically) three ways to handle events. You can use:`),
|
2023-11-24 16:16:08 +01:00
|
|
|
|
el("ul").append(
|
|
|
|
|
el("li").append( el("code", `el("button", { textContent: "click me", "=onclick": "console.log(event)" })`)),
|
|
|
|
|
el("li").append( el("code", `el("button", { textContent: "click me", onclick: console.log })`)),
|
|
|
|
|
el("li").append( el("code", `el("button", { textContent: "click me" }, on("click", console.log))`))
|
2023-11-17 16:15:26 +01:00
|
|
|
|
),
|
2024-11-22 10:20:59 +01:00
|
|
|
|
el("p").append(...T`
|
|
|
|
|
In the first example we force to use HTML attribute (it corresponds to
|
|
|
|
|
${el("code", `<button onclick="console.log(event)">click me</button>`)}). ${el("em", t`Side note:
|
|
|
|
|
this can be useful in case of SSR.`)} To study difference, you can read a nice summary here:
|
|
|
|
|
${el("a", { textContent: "GIST @WebReflection/web_events.md", ...references.web_events })}.
|
|
|
|
|
`)
|
2023-11-24 16:16:08 +01:00
|
|
|
|
),
|
2023-11-18 14:23:18 +01:00
|
|
|
|
|
2024-11-22 10:20:59 +01:00
|
|
|
|
el(h3, t`Addons`),
|
|
|
|
|
el("p").append(...T`
|
|
|
|
|
From practical point of view, ${el("em", t`Addons`)} are just functions that accept any HTML element as
|
|
|
|
|
their first parameter. You can see that the ${el("code", "on(…)")} fullfills this requirement.
|
|
|
|
|
`),
|
|
|
|
|
el("p").append(...T`
|
|
|
|
|
You can use Addons as ≥3rd argument of ${el("code", "el")} function. This way is possible to extends your
|
|
|
|
|
templates by additional (3rd party) functionalities. But for now mainly, you can add events listeners:
|
|
|
|
|
`),
|
2023-11-24 16:16:08 +01:00
|
|
|
|
el(example, { src: fileURL("./components/examples/events/templateWithListeners.js"), page_id }),
|
2024-11-22 10:20:59 +01:00
|
|
|
|
el("p").append(...T`
|
|
|
|
|
As the example shows, you can also provide types in JSDoc+TypeScript by using global type
|
|
|
|
|
${el("code", "ddeElementAddon")}. Also notice, you can use Addons to get element reference.
|
|
|
|
|
`),
|
|
|
|
|
el(h3, t`Life-cycle events`),
|
|
|
|
|
el("p").append(...T`
|
|
|
|
|
Addons are called immediately when the element is created, even it is not connected to live DOM yet.
|
|
|
|
|
Therefore, you can understand the Addon to be “oncreate” event.
|
|
|
|
|
`),
|
|
|
|
|
el("p").append(...T`
|
|
|
|
|
The library provide three additional live-cycle events corresponding to how they are named in a case of
|
2024-11-22 16:26:42 +01:00
|
|
|
|
custom elements: ${el("code", "on.connected")}, ${el("code", "on.disconnected")} and ${el("code",
|
|
|
|
|
"on.attributeChanged")}.
|
2024-11-22 10:20:59 +01:00
|
|
|
|
`),
|
2023-11-24 16:16:08 +01:00
|
|
|
|
el(example, { src: fileURL("./components/examples/events/live-cycle.js"), page_id }),
|
2024-11-22 10:20:59 +01:00
|
|
|
|
el("p").append(...T`
|
|
|
|
|
For Custom elements, we will later introduce a way to replace ${el("code", "*Callback")} syntax with
|
|
|
|
|
${el("code", "dde:*")} events. The ${el("code", "on.*")} functions then listen to the appropriate
|
2024-11-22 16:26:42 +01:00
|
|
|
|
Custom Elements events (see ${el("a", { textContent: t`Custom element lifecycle callbacks | MDN`,
|
|
|
|
|
...references.mdn_customElement })}).
|
2024-11-22 10:20:59 +01:00
|
|
|
|
`),
|
|
|
|
|
el("p").append(...T`
|
2024-11-22 16:26:42 +01:00
|
|
|
|
But, in case of regular elemnets the ${el("a", references.mdn_mutation).append(el("code",
|
|
|
|
|
"MutationObserver"), " | MDN")} is internaly used to track these events. Therefore, there are some
|
|
|
|
|
drawbacks:
|
2024-11-22 10:20:59 +01:00
|
|
|
|
`),
|
2023-11-24 16:16:08 +01:00
|
|
|
|
el("ul").append(
|
2024-11-22 10:20:59 +01:00
|
|
|
|
el("li").append(...T`
|
|
|
|
|
To proper listener registration, you need to use ${el("code", "on.*")} not \`on("dde:*", …)\`!
|
|
|
|
|
`),
|
|
|
|
|
el("li").append(...T`
|
|
|
|
|
Use sparingly! Internally, library must loop of all registered events and fires event properly.
|
|
|
|
|
${el("strong", t`It is good practice to use the fact that if an element is removed, its children are
|
|
|
|
|
also removed!`)} In this spirit, we will introduce later the ${el("strong", t`host`)} syntax to
|
|
|
|
|
register, clean up procedures when the component is removed from the app.
|
|
|
|
|
`),
|
2024-01-31 14:37:19 +01:00
|
|
|
|
),
|
2024-11-22 10:20:59 +01:00
|
|
|
|
el("p").append(...T`
|
|
|
|
|
To provide intuitive behaviour, similar also to how the life-cycle events works in other
|
|
|
|
|
frameworks/libraries, deka-dom-el library ensures that ${el("code", "on.connected")} and
|
|
|
|
|
${el("code", "on.disconnected")} are called only once and only when the element, is (dis)connected to
|
|
|
|
|
live DOM. The solution is inspired by ${el("a", { textContent: "Vue", ...references.vue_fix })}. For using
|
|
|
|
|
native behaviour re-(dis)connecting element, use:
|
|
|
|
|
`),
|
2024-01-31 14:37:19 +01:00
|
|
|
|
el("ul").append(
|
2024-11-22 16:26:42 +01:00
|
|
|
|
el("li").append(...T`custom ${el("code", "MutationObserver")} or logic in (dis)${el("code",
|
|
|
|
|
"connectedCallback")} or…`),
|
2024-11-22 10:20:59 +01:00
|
|
|
|
el("li").append(...T`re-add ${el("code", "on.connected")} or ${el("code", "on.disconnected")} listeners.`)
|
2024-01-31 14:37:19 +01:00
|
|
|
|
),
|
2023-11-24 16:16:08 +01:00
|
|
|
|
|
2024-11-22 10:20:59 +01:00
|
|
|
|
el(h3, t`Final notes`),
|
|
|
|
|
el("p", t`The library also provides a method to dispatch the events.`),
|
2023-11-24 16:16:08 +01:00
|
|
|
|
el(example, { src: fileURL("./components/examples/events/compareDispatch.js"), page_id }),
|
2024-11-22 16:26:42 +01:00
|
|
|
|
|
2023-11-29 18:25:21 +01:00
|
|
|
|
el(mnemonic)
|
2023-11-17 16:15:26 +01:00
|
|
|
|
);
|
|
|
|
|
}
|