4 ms·
I came up with something similar recently, except it doesn't use template elements. It just uses functions and template literals. The function returns a string,
by lylejantzi3rd 1y ago
I came up with something similar recently, except it doesn't use template elements. It just uses functions and template literals. The function returns a string, which gets dumped into an existing element's innerHTML. Or, a new div element is created to dump it into. Re-rendering is pretty quick that way.
A significant issue I have with writing code this way is that the functions nest and it becomes very difficult to make them compose in a sane way.
function printPosts(posts) {
let content = ""
posts.forEach((post, i) => {
content += printPost(post)
})
window.posts.innerHTML = content
}
function printPost(post) {
return `
<div class="post" data-guid="${post.guid}">
<div>
<img class="avatar" src="https://imghost.com${post.avatar.thumb}"/>
</div>
<div class="content">
<div class="text-content">${post.parsed_text}</div>
${post?.image_urls?.length > 0 ? printImage(`https://imghost.com${post.image_urls[0].original}`) : ''}
${post?.url_preview ? `<hr/><div class="preview">${printPreview(post.url_preview)}</div>` : ''}
${post?.quote_data ? `<hr/><div class="quote">${printQuote(post.quote_data)}</div>` : ''}
${post?.filtered ? `<div>filtered by: <b>${post.filtered}</b></div>` : ''}
</div>
</div>
`
}
- hyperhello 1y agoI like it. Not only does it move the UI into JavaScript, but it moves the scripting into the HTML!
- Koffiepoeder 1y agoHave a feeling this will lead to XSS vulnerabilities though.
- MrJohz 1y agoHow do you update the html when something changes? For me, that's the most interesting question for these sorts of micro-frameworks - templating HTML or DOM nodes is super easy, but managing state and updates is hard.
- dleeftink 1y agoI find the coroutine/generator approach described in a series of posts by Lorenzo Fox/Laurent Renard to be a promising alternative[0]. It takes a little to wrap your head around, but essentially structures component rendering to follow the natural lifecycle of a generator function that takes as input the state of a previous yield, and can be automatically cleaned up by calling `finally` (you can observe to co-routine state update part in this notebook[1]). This approach amounts to a really terse co-routine microframework [2]. [0]: https://lorenzofox.dev/posts/component-as-infinite-loop/#:~:text=usual%20next.-,Altogether,-%3A https://lorenzofox.dev/posts/component-as-infinite-loop/#:~:... [1]: https://observablehq.com/d/940d9b77de73e8d6 https://observablehq.com/d/940d9b77de73e8d6 [2]: https://github.com/lorenzofox3/cofn https://github.com/lorenzofox3/cofn
- lylejantzi3rd 1y agoI call printPosts with the new post data. It rewrites the whole chunk in one go, which is pretty snappy. I haven't decided how I'm going to handle more granular updates yet, like comment count or likes.
- MrJohz 1y agoYeah, that's a pretty common approach. Unfortunately, browsers aren't very good at doing patch updates, so it'll completely reset any UI elements in the region being rerendered. It also will make it hard to scope anything you want to do to an individual DOM element. If you want granular updates, for example, you want to be able to do something like `document.querySelector(???)` and be certain it's going to refer to, say, a specific text input in your `printPost` template, without worrying about accessing the inputs created by other instances of the `printPost` template. You can do that with unique IDs, but it's fiddly and error-prone.
- deleted 1y ago[deleted]
- econ 1y agoI prefer something like this before building the template string. image = post.image_urls?[0] || ""; Then have the printImage function return an empty string if the argument is an empty string. ${printImage(image)} Easier on the eyes.
- chrismorgan 1y agoThis is begging for injection attacks. In this case, for example, if parsed_text and filtered can contain < or &, or if post.guid or post.avatar.thumb can contain ", you’re in trouble. Generating serialised HTML is a mug’s game when limited to JavaScript. Show me a mature code base where you have to remember to escape things, and I’ll show you a code base with multiple injection attacks.
- foota 1y agoYeah, OPs code is asking for pain. I suspect there are now developers who've never had to generate html outside the confines of a framework and so are completely unaware of the kinds of attacks you need to protect yourself against. You can do it from scratch, but you essentially need to track provenance of strings (either needs to be escaped and isn't html, e.g., user input, or html, which is either generated and with escaping already done or static code). It seems like you could build this reasonably simply by using tagged template literals and having e.g., two different Types of strings that are used to track provenance.
- brigandish 1y agoThus recreating Perl’s taint mode. Everything new is old.
- lylejantzi3rd 1y agoPosts are sanitized on the server side. This is client side code.
- hombre_fatal 1y agoServer-side sanitization means that your view code is inherently vulnerable to injection. You'll notice in modern systems you don't sanitize data in the database and you don't have to manually sanitize when rendering frontend code. It's like that for a reason. Server-side sanitization and xss injection should be left in the 2000s php era.
- 1y ago
- spankalee 1y agoYou should really check out lit-html[1]. It's not a framework like this README claims. It just renders template with template literals, but it does so with minimal DOM updates and safely. And it has a number of features for declaratively adding event handlers, setting properties, and dealing with lists. [1]: https://lit.dev/docs/libraries/standalone-templates/ https://lit.dev/docs/libraries/standalone-templates/