5 ms·
I've found much use out of custom elements. On nice use, that took me maybe 30m to code up, is a `<remote-fragment>` element. <remote-fragment src=/fragme
by lelanthran 3y ago
I've found much use out of custom elements.
On nice use, that took me maybe 30m to code up, is a `<remote-fragment>` element.
<remote-fragment src=/fragments/topbar.htmlf />
simply retrieves the specified file and adds it to the DOM where the `remote-fragment` is declared.
No need for server-side scripting to include common parts of the HTML page.
There's a bunch of similar use-cases currently satisfied by fat client frameworks or server-side languages that simply go away when you have custom elements.
So, yeah, you can do components with webcomponents they way you do much of react components, but there's some stuff that you can do with webcomponents that just makes life a little bit better[1].
[1] Experimenting with propagating state throughout a (client-side) application using only declarations in HTML, with custom elements performing stuff.
- nesarkvechnep 3y agoIt seems like you reinvented Edge Site Includes (https://en.m.wikipedia.org/wiki/Edge_Side_Includes https://en.m.wikipedia.org/wiki/Edge_Side_Includes).
- lelanthran 3y agoI like to think that I didn't reinvent it (just like react, et al didn't reinvent it), I only refined the concept into the simplest form that works :-) After all, a custom element that takes a front-end newcomer 30m to write is absolutely preferable to a large infrastructure-based standard that, after 22 years, is still not included in the spec. Look at the options for including HTML fragments in web pages: 1. Complex client-side react/vue/etc, 2. A server language , 3. A build-step including a non-spec bundler/packer, 4. A `<remote-fragment src="..." /> My point is that webcomponents make quite a lot of complicated things redundant for particular use-cases. As time goes on I see the fat front-end frameworks having more of their functionality being replaced with less complex (to use) custom elements. (Hence my experiments with value propagation using custom elements)
- nesarkvechnep 3y agoRefined the concept into the simplest form that works… as long as you have JavaScript enabled.
- lelanthran 3y agoWhat's the alternative other than simply doing without this feature?
- Quekid5 3y agoE.g. Apache's Server Side Includes[0] allows for <!--#include virtual="/footer.html" --> Tbf, you did specify 'server language' in a post further up the comment chain, but the the thing is that it doesn't have to be much more complicated that SSI -- assuming it's available for your deployment target, of course. (SSI obviously does not require JavaScript.) [0] https://httpd.apache.org/docs/current/howto/ssi.html https://httpd.apache.org/docs/current/howto/ssi.html
- lelanthran 3y agoI've used those before, but I feel that having something that works within the spec, independent of any particular implementation's extensions is much more valuable for longevity. Sure, you can use $A if using $B server, or use $C when using $D language, or use $E when using $F framework, or use $G when using $H reverse-proxy, or use $I when using $J CDN, or use $K when using $L hosted cloud service ... Or you can just use what's in the specification which works on all of the above, but won't work on clients with Javascript disabled. It's a "choose your poison" scenario.
- mewpmewp2 3y agoTechnically you could also just write a bit of JS within the HTML to do without needing any of the first 3. You could querySelectorAll and check the src attribute etc, then fetch the content put it in the innerHTML. <remote-fragment src="https://..." /> <script> document.querySelectorAll("remote-fragment").forEach(async (el) => { const src = el.getAttribute("src"); const result = await fetch(src).then((res) => res.text()); el.innerHTML = result; }); </script> Or if you want to react to any such component inserted at times down the line or src attribute changes, you could use MutationObserver.
- big_paps 3y agoCool. But is it readable from a search engine?
- lelanthran 3y agoNot a priority for webapps. In websites, search engines ignoring the common elements that you would use this for, such as navbars, isn't a deal-breaker. When I search Google for keyword FOO, results that have FOO only in the elements common to all web pages are useless results. I'm typically looking for FOO in the content, not in the navigational elements or other common elements.
- DylanSp 3y agoCan you elaborate a bit more on the example in your footnote about propagating client-side state? Does that provide functionality similar to Alpine.js's data objects/stores?
- lelanthran 3y agoSure, but bear in mind it's only an experiment, so I do not expect this to be long-lived, or if it is, I do not expect it to look the same way as it does now, and even if either of the above remain true, I still think that alternatives such as htmx are preferably for multiple reasons, bar one (explained in the last paragraph of this post). IOW, I expect this to be an experiment that I eventually throw away after having gained some insight. The `ps` in `psjs` below means "publish/subscribe". The big draw for me about htmx is that common usage does not require knowledge of Javascript! This means that htmx (and my set of custom elements) does not require the creator of the front-end to know that npm even exists, much less how to use it. The same goes for Virtual DOMs, Hooks, tree-shaking/web-packing, promises and async, functions, and the whole container-ship full of arbitrary things that common front-end stacks need you to know. My custom elements require javascript (for now), and require that the user know what is meant by 'publish a message onto a queue' and 'subscribe to a queue for messages of a particular subject'. ------------------------------------- I have at least four custom elements: psjs-tree, psjs-bind, psjs-subscribe and psjs-publish. I also have two JS functions, `publish` and `subscribe`: `publish` publishes an arbitrary payload (a JS object) to a channel (string) with a subject (also of string type). `subscribe` registers a callback function for a channel (string) with a pattern for the subject. These are all global. A piece of code could do: subscribe("ERROR", "*", (sender, subject, payload) => { dialog.innerHTML = `${sender.id}: Error ${subject} ${payload.errMessage}`; dialog.showModal(); }); and then any code, anywhere else can do `publish(this, "ERROR", "Rpc Request", { errMsg: "Rxed 404"});` My web components psjs-publish and psjs-subscribe execute those two functions (for publish, there is an `onevent` attribute). The psjs-publish component uses the closest psjs-tree ancestor to determine which fields go into the payload. The fields are set by psjs-bind. The psjs-subscribe does the opposite of psjs-publish, in that it uses the closest psjs-tree ancestor to determine which fields of the payload should be used to update the contents or values of the psjs-tree descendents. For example, a form that can have the values reset by some other code (say, fetching them from a server) and can have the values submitted: <psjs-tree> <psjs-subscribe channel="MYFORM" subject="update-fields"> </psjs-subscribe> <psjs-bind for="team-name"> </psjs-bind> <psjs-bind for="notifications"> </psjs-bind> <psjs-bind for="user-name"> </psjs-bind> <psjs-bind for="user-email"> </psjs-bind> <form> <div id="title"> Example</div> <div id="team-name"> </div> <div id="notifications"> </div> <input id="user-name"> </input> <input id="user-email"> </input> <psjs-publish onevent="click" channel=MYFORM subject=submit-fields> <button>Submit</button> </psjs-publish> </form> </psjs-tree> Of course, this means that I have a line of js somewhere that looks like this: subscribe("MYFORM", "submit-*", (sender, subject, payload) => { /* send payload to server */ }); And another that looks like this: const rsp = successfulFetchFromServer(); publish(this, "MYFORM", "update-fields", { "team-name": rsp.teamName, ... }); This is still very much a WIP, and I'm playing with having custom elements for performing RPC without using the publish/subscribe functions; this is so that, during usage of these components, there will be zero JS to write. Right now there is still the publish/subscribe calls that intercept messages and tx/rx messages from the server to local components. I'd like it to look like this eventually: <rpc-tree> <rpc-rx subject="Some Subject"> <rpc-bind for="team-name"> </rpc-bind> <rpc-bind for="notifications"> </rpc-bind> <rpc-bind for="user-name"> </rpc-bind> <rpc-bind for="user-email"> </rpc-bind> <form> <div id="title"> Example</div> <div id="team-name"> </div> <div id="notifications"> </div> <input id="user-name"> </input> <input id="user-email"> </input> <rpc-tx onevent="click" href="/some/path/to/submit" subject="Some Subject"> <button>Submit</button> </rpc-tx> </form> </rpc-tree> The psjs-subscribe/publish elements are still useful to propagate value changes locally within the client, but for non-local state an RPC set of elements seems preferable. All in all (and I realise that I wrote an exceptionally long post, so sorry!), I feel that htmx is better right now, but ... it forces the creation of a backend-for-frontend layer, so you have the layers of front-end -> backend-for-front-end -> API. With RPC calls only, I don't need to write the middle layer and can simply use the API directly from the client application, especially when using the custom element for specifying fragments.
- jbergens 3y agoSounds like htmx. https://htmx.org/essays/right-click-view-source/ https://htmx.org/essays/right-click-view-source/
- jbergens 3y agoMeant to link to the root but can't change now. https://htmx.org/ https://htmx.org/
- lelanthran 3y agoIt is, but only for the subset I think is absolutely necessary.