2024-05-22 21:43:49 +02:00
export const info = {
title : "Events and Addons" ,
description : "Using not only events in UI declaratively." ,
} ;
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-05-31 14:14:48 +02:00
const references = {
/** element.addEventListener() */
mdn _listen : {
title : "MDN documentation page for elemetn.addEventListener" ,
href : "https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener" ,
} ,
/** AbortSignal+element.addEventListener */
mdn _abortListener : {
title : "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 : {
href : "https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks"
} ,
/** 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 : "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 (
el ( "h2" , "Listenning to the native DOM events and other Addons" ) ,
el ( "p" ) . append (
"We quickly introduce helper to listening to the native DOM events." ,
" " ,
"And library syntax/pattern so-called " , el ( "em" , "Addon" ) , " to" ,
" incorporate not only this in UI templates declaratively."
) ,
2023-11-24 17:02:16 +01:00
el ( code , { src : fileURL ( "./components/examples/events/intro.js" ) , page _id } ) ,
2023-11-24 16:16:08 +01:00
el ( h3 , "Events and listenners" ) ,
el ( "p" ) . append (
"In JavaScript you can listen to the native DOM events of the given element by using " ,
2024-05-31 14:14:48 +02:00
el ( "a" , references . mdn _listen ) . append ( el ( "code" , "element.addEventListener(type, listener, options)" ) ) , "." ,
2023-11-24 16:16:08 +01:00
" " ,
"The library provides an alternative (" , el ( "code" , "on" ) , ") accepting the differen order" ,
" of the arguments:"
) ,
el ( example , { src : fileURL ( "./components/examples/events/compare.js" ) , page _id } ) ,
el ( "p" ) . append (
"…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 (
"The other difference is that there is " , el ( "strong" , "no" ) , " " , el ( "code" , "off" ) , " function." ,
" " ,
2024-05-31 14:14:48 +02:00
"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 (
el ( "p" , "So, there are (typically) three ways to handle events. You can use:" ) ,
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
) ,
el ( "p" ) . append (
2023-11-24 16:16:08 +01:00
"In the first example we force to use HTML attribute (it corresponds to " , el ( "code" , ` <button onclick="console.log(event)">click me</button> ` ) , ")." ,
2023-11-18 14:23:18 +01:00
" " ,
2023-11-24 16:16:08 +01:00
el ( "em" , "Side note: this can be useful in case of SSR." ) ,
2023-11-18 14:23:18 +01:00
" " ,
2024-05-31 14:14:48 +02:00
"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
2023-11-24 16:16:08 +01:00
el ( h3 , "Addons" ) ,
el ( "p" ) . append (
"From practical point of view, " , el ( "em" , "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 (
"You can use Addons as ≥3rd argument of " , el ( "code" , "el" ) , " function. This way is possible to extends" ,
" you templates by additional (3rd party) functionalities. But for now mainly, you can add events listeners:"
) ,
el ( example , { src : fileURL ( "./components/examples/events/templateWithListeners.js" ) , page _id } ) ,
el ( "p" ) . append (
"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 , "Life-cycle events" ) ,
el ( "p" ) . append (
"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 (
"The library provide three additional live-cycle events corresponding to how they are named in" ,
" a case of custom elements: " , el ( "code" , "on.connected" ) , ", " , el ( "code" , "on.disconnected" ) ,
" and " , el ( "code" , "on.attributeChanged" ) , "."
) ,
el ( example , { src : fileURL ( "./components/examples/events/live-cycle.js" ) , page _id } ) ,
el ( "p" ) . append (
"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" ,
2024-05-31 14:14:48 +02:00
" listen to the appropriate Custom Elements events (see " , el ( "a" , { textContent : "Custom element lifecycle callbacks | MDN" , ... references . mdn _customElement } ) , ")."
2023-11-24 16:16:08 +01:00
) ,
el ( "p" ) . append (
2024-05-31 14:14:48 +02:00
"But, in case of regular elemnets the " , el ( "a" , references . mdn _mutation ) . append ( el ( "code" , "MutationObserver" ) , " | MDN" ) ,
2023-11-24 16:16:08 +01:00
" is internaly used to track these events. Therefore, there are some drawbacks:" ,
) ,
el ( "ul" ) . append (
el ( "li" ) . append (
"To proper listener registration, you need to use " , el ( "code" , "on.*" ) , " not `on(\"dde:*\", …)`!"
2023-11-18 14:23:18 +01:00
) ,
2023-11-24 16:16:08 +01:00
el ( "li" ) . append (
"Use sparingly! Internally, library must loop of all registered events and fires event properly." ,
2023-11-18 14:23:18 +01:00
" " ,
2023-11-24 16:16:08 +01:00
el ( "strong" , "It is good practice to use the fact that if an element is removed, its children are also removed!" ) ,
2023-11-21 17:19:59 +01:00
" " ,
2023-11-24 16:16:08 +01:00
"In this spirit, we will introduce later the " , el ( "strong" , "host" ) , " syntax to register" ,
" clean up procedures when the component is removed from the app."
2023-11-21 17:19:59 +01:00
) ,
2023-11-24 16:16:08 +01:00
) ,
2024-01-31 14:37:19 +01:00
el ( "p" ) . append (
"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" ,
2024-05-31 14:14:48 +02:00
" is (dis)connected to live DOM. The solution is inspired by " , el ( "a" , { textContent : "Vue" , ... references . vue _fix } ) , "." ,
2024-01-31 14:37:19 +01:00
" For using native behaviour re-(dis)connecting element, use:"
) ,
el ( "ul" ) . append (
2024-05-23 22:18:19 +02:00
el ( "li" ) . append ( "custom " , el ( "code" , "MutationObserver" ) , " or logic in (dis)" , el ( "code" , "connectedCallback" ) , " or…" ) ,
2024-01-31 14:37:19 +01:00
el ( "li" ) . append ( "re-add " , el ( "code" , "on.connected" ) , " or " , el ( "code" , "on.disconnected" ) , " listeners." )
) ,
2023-11-24 16:16:08 +01:00
el ( h3 , "Final notes" ) ,
el ( "p" , "The library also provides a method to dispatch the events." ) ,
el ( example , { src : fileURL ( "./components/examples/events/compareDispatch.js" ) , page _id } ) ,
2023-11-29 18:25:21 +01:00
el ( mnemonic )
2023-11-17 16:15:26 +01:00
) ;
}