return to table of content

Mastering DOM manipulation with vanilla JavaScript

mg
29 replies
1d3h

These days, I get by with just two dependencies. One is dqs.js:

https://github.com/no-gravity/dqs.js

Which turns document.querySelector('.some .thing') into dqs('.some .thing').

The other one is Handlebars:

https://github.com/handlebars-lang/handlebars.js/

Which I use for all my frontend templating needs.

bambax
17 replies
1d2h

Instead of importing dsq you can do this

    const $ = document.querySelector.bind(document);
    const $$ = document.querySelectorAll.bind(document);
xeckr
5 replies
1d

Or

const $ = (query) => document.querySelector(query);

const $$ = (query) => document.querySelectorAll(query);

(How do I write monospace on HN?)

bambax
2 replies
22h13m

How do I write monospace on HN?

Line that start with four spaces

    like this
xeckr
1 replies
20h58m

  Looks like 2 spaces are sufficient. But I sympathize with 4-space indentation.
bambax
0 replies
20h47m

Indeed! I've always thought it was 4, because... Markdown, probably?

chrismorgan
1 replies
22h12m

(How do I write monospace on HN?)

Two-space indentation.

xeckr
0 replies
21h1m

  Thank you
crtasm
5 replies
23h15m

Is there a way to make this also work as a function? e.g. myTable.$$('tr').foreach()

mg
4 replies
23h2m

    import {qsA} from './dqs.js';

    rows = qsA(myTable, 'tr');
crtasm
3 replies
22h38m

Thanks but the point is being able to chain it:

myTable.qsA('tr')

TypeError: myTable.qsA is not a function

mg
2 replies
22h7m

Well, since html elements already have a querySelectorAll function, you could just assign it to a new method "qsA":

    HTMLElement.prototype.qsA = HTMLElement.prototype.querySelectorAll; 
Now myTable.qsA('tr') will work. You can try it right here on the HN page in the browser console:

    HTMLElement.prototype.qsA = HTMLElement.prototype.querySelectorAll; 
    document.querySelector('#hnmain').qsA('tr'); 
Why do you prefer chaining?
xeckr
0 replies
20h49m

You'll have to do this in addition to one of the previously discussed solutions if you want something like document.qsA since document isn't an HTMLElement.

crtasm
0 replies
21h11m

Perfect, thankyou!

I suppose habit plus I find it easier to grok in that order, targetelement->childelements->foreach.

gmiller123456
2 replies
1d2h

That's pretty much what that include file does, but with "dqs" rather than "$". I'm not a fan of using the $ as it might confuse others that you're using JQuery.

chrismorgan
0 replies
22h13m

$ is what I would expect and prefer, because it’s what I’ve been using in my browser dev tools for over fifteen years (ah, Firebug; fond reminiscences). If your document doesn’t use the names, you’ll get implementations roughly equivalent to these:

  function $(selector, element = document) {
      return element.querySelector(selector);
  }

  function $$(selector, element = document) {
      return Array.from(element.querySelectorAll(selector));
  }
Reference on more magic available in the console (there’s a lot of good stuff):

https://firefox-source-docs.mozilla.org/devtools-user/web_co... (I think “:help” is my new favourite: opens that URL, which is not particularly discoverable)

https://developer.chrome.com/docs/devtools/console/utilities...

bambax
0 replies
1d1h

Wouldn't that be a feature? ;-)

throw555chip
0 replies
1d2h

That's a neat shortcut, I prefer vanilla JS too.

mg
0 replies
1d2h

That's what I did before I put those lines (and some more) into dqs.js

dylan604
6 replies
1d2h

are we really concerned about the shortness of the code for the worse tradeoff of being less readable?

how much is truly saved by this? these seem like the things of a solo dev vs building something for other people to follow

mg
4 replies
1d2h

What's wrong with solo devs?

You are talking to me on a site done by one.

This is the frontend code:

https://news.ycombinator.com/hn.js

throw555chip
0 replies
1d2h

That's wild, never really looked into it, reminds me how a C programmer thinks. I'm mostly backend but get involved with frontend every now and then. Tried to avoid Angular and React but now trying to teach myself Angular. AngularJS was nice. Angular sucks, very complicated for doing the same things I can do with vanilla HTML/CSS/JS, but that's what companies seem to be hiring for.

evbogue
0 replies
1d2h

hn would benefit from the ael library

dylan604
0 replies
1d

I've been a solo dev as well. But unless you're going to live forever, the code should be maintainable by someone else. You can be a solo dev that's impossible to ever work with, or you can maintain good practices so that other people can play along as well.

deathanatos
0 replies
20h39m

… is that a good example?

… upvote/downvotes are does by fetching an "image", i.e., it does a GET request with side-effects! … also seems like a trivial CSRF vuln, too…

von_lohengramm
0 replies
1d2h

There's a reason why web developers have the reputation they do.

r-spaghetti
1 replies
1d2h

Can you use Handlebars in Cloudflare workers? EJS was not accepted and string literals are string literals.

mg
0 replies
1d1h

handlebars.js runs in the browser:

https://plnkr.co/edit/LXb5TOMoyAxdbTxB

nicoburns
0 replies
1d2h

These days? This is what we were using 10 years ago before React came about! It works fine, but performance can be a problem in large apps as handlebars just naively recreates entire DOM trees rather than diffing.

evbogue
0 replies
1d2h

Combine this with https://github.com/dominictarr/hscrpt and you'll be building websites in no time!

snide
27 replies
1d3h

Excellent list and examples. Browsing through a dozen or so of these I'm amazed at the care and detail in the examples. They are not only functional, but provide very solid UI work as well.

As I've shifted away from platforms like React to smaller, minimalist implementations, I often have trouble finding ways for how to do complex patterns in standard JS. It's funny when you look at the code and think... huh, that's actually a lot easier than importing a huge library with way too many props :)

Thanks to the author for putting this together.

bob1029
25 replies
1d2h

It's funny when you look at the code and think... huh, that's actually a lot easier than importing a huge library with way too many props

I had this realization after the 3rd or 4th RiotJS major version update. It started getting harder to do it the opinionated way. I realized that every minimalistic JS framework would eventually suffer this fate as totally innocent feature requests gradually accumulate into a monstrous pile of hooks, events and other side-effect bandaids.

I don't even use jQuery anymore. I will use things like ES modules to help organize code, but you don't need to run a goddamn npm command to use any of that technology. All of this is a global browser standard, not something you have to vendor in.

I look at MDN as the Bible these days. If you take a leap of faith, it will carry you almost 100% of the way. The frameworks will cripple you after a certain point. I can't say this would have been true ~5 years ago. Things have shifted rapidly with browser APIs converging on a powerful set of primitives (modules, flexbox, grid, etc) that are now very consistent across platforms.

reactordev
24 replies
1d2h

I like the fact that you said “I can’t say this would have been true ~5 years ago”.

React et al. exist because there weren’t global standards around these things and composability suffered. Now that we are all playing the same chorus, these frameworks provide little more than component libraries to reuse. This can be done with ES modules. JSX is why most React folks stick with React, not knowing they can use Preact or just NakedJSX if they wish.

I just wanted to verbally concur that vanilla JS is more than capable of doing everything you need. Custom tags. Shadow dom. Composed UI. etc. if you are ok with returning HTMLElement vs a JSX closure.

sesm
5 replies
1d1h

React exists because Facebook needed to sync DOM elements in different parts of webpage with a single source of state. There is still no global standard that solves this problem.

reactordev
2 replies
22h4m

BS there’s custom elements and classes and the ability to target them with a selector. This is standard now. Web components.

nicoburns
0 replies
21h29m

Web components don't really do the same thing. If you use web components then you still need to manage the DOM manually.

MrJohz
0 replies
21h11m

Web components solve encapsulation of structure, style, and behaviour within a single component, but they don't solve the larger state problems, because web components don't come with any sort of state management beyond what you can do with normal Javascript.

React does provide components, but there's not so much different in React components than any other component system. The main reason to use React is the state management it provides on top of those components, making sure that state is in sync across the whole application.

In my experience, the hard part of larger applications is that handling of application state. Rendering data can be done in umpteen different ways, and they're all pretty much good enough, but handling application state in such a way that every component remains in sync with all other components is hard.

marcosdumay
0 replies
1d

You mean state handlers? Those are one of the main reasons people adopted OOP at the 80's.

The observer pattern got pretty much standardized by the GoP book, but people overwhelmingly prefer to run some non-standard specialized thing, as it's usually much simpler. It's a pretty much solved problem since way before a lot of current developers were even born.

All of (ok, a lot of) the React's complexity comes from it being a generic library that must support every kind of usage. Any standard would have to be as complex.

gedy
0 replies
1d

Yeah I think some people glorify "simple", low level solutions, which work fine for sprinkles of interactivity. But if I'm building a non trivial UI application - give me a framework please!

wayfinder
4 replies
1d

No, React exists because there was a need for a template engine that’s easy to use.

There is still no global standard for templates.

Just like mustache, Twig, Java Server Pages, Jinja, ERB… every language has this problem. You have a bunch of components written in HTML and you need to mix and match them together in a bigger unit. React+JSX let you do that easier than appending DOM components. Before React, you’d use different libraries that did what React did.

The ONLY major language I can think of that has templating baked in is PHP.

Joeri
2 replies
23h50m

HTML has built-in templates now, and custom elements (web components) provide composability. If the built-in templating is not quite powerful enough (it has no logic or variables) there’s developit/htm which is basically JSX but parsing in the browser.

React is still easier, but it comes at a maintainability tax because you have to keep upgrading to new react versions and port to new react wrapper framework versions if you want to keep a codebase in active development. Vanilla web development has no such problem because web standards remain backwards compatible.

For a small project where I don’t care about SEO / server-side rendering I absolutely prefer vanilla web dev over React now.

wayfinder
0 replies
21h47m

Yes, it does! But while I have not used them yet, the approach used by them (via SGML tags) is not new and many libraries / XLST have done it before in the past 20+ years and I find the approach usable at best. It's too verbose for my taste and it never really caught on, and I find it hard to think that it would catch on this time.

nicoburns
0 replies
21h26m

React is still easier, but it comes at a maintainability tax because you have to keep upgrading to new react versions and port to new react wrapper framework versions if you want to keep a codebase in active development.

There has been one breaking version of React (the recent React 18) in it's 8 years of history. Upgrading React versions is a non-issue.

Wrapper frameworks like next.js can come with a much higher maintenance cost, but you don't need to use those to use React (and I think most people don't).

johnmaguire
0 replies
22h38m

The ONLY major language I can think of that has templating baked in is PHP.

Depends on what you mean exactly. For example, Go ships with templates. They don't mix HTML markup and Go code directly though.

nine_k
4 replies
1d2h

One of the points of React vs e.g. jQuery was the ability to do efficient updates in large DOM.trees.

Did something arrive to plain JS APIs to allow this in a simple enough way?

wruza
1 replies
23h44m

the ability to do efficient updates in large DOM.trees

It was the ability to do efficient updates based on large VDOM diffs. The speed of updates is the same between a React VDOM-diff update plan and a direct update of the same size based on e.g. observers.

nine_k
0 replies
21h57m

Yes, that's the point: computing what needs to change using observers quickly becomes hard. React allows you to run all updates against an unchanged view; observers can interact with each other's updates.

While React proper may be too large, I find the approach indispensable for more complex interfaces. One can pick Preact instead.

But it's indeed overkill for adding small bits of interactivity to large, mostly static documents.

r-spaghetti
0 replies
1d2h

It depends on the type of relations between the updates you want to make, for example adding/removing a class can group DOM elements. Updating all elements with this class is easy.

kevindamm
0 replies
1d1h

Templates and shadow DOM help a lot with creating and cloning of large subtrees, but time to first render and caution around page re-layout still require attention to do well at scale.

irrational
2 replies
1d2h

I though React, et al were originally created as a better way to manage DOM updates (shadow DOM). Has that need gone away?

nicoburns
0 replies
1d2h

Not at all. Without React (or a similar framework) you are still left with the options of:

- Manually keep track of UI state (which is complex, and often leads to hard to maintain spaghetti code)

- Recreate an entire DOM tree every time you re-render (which is slow to the extent that it will often lead to performance problems in practice). Apparently this is not as slow as it used to (so you could probably get away with this sometimes - and you could before), but it's still generally a better idea to use a framework as you get better performance for very low cost.

There are plenty of non-React frameworks that will you these benefits too. And many of them are much smaller. I think the reason to use React specifically is more "business reasons" such assurances that the framework will continue to be maintained, library ecosystem, developer pool, etc. Other frameworks are often technically superior, the difference just isn't that great.

evan_
0 replies
1d2h

React et al use a virtual DOM, not shadow DOM (unless you specifically render it to shadow DOM).

They are a better* way to manipulate DOM in that you no longer need to manipulate the DOM, you just build a function that returns what the DOM should look like and it figures out what transforms need to happen.

*They’re pitched as a better way, and I think it’s better, but people can reasonably disagree

nicoburns
1 replies
1d2h

JSX is why most React folks stick with React, not knowing they can use Preact or just NakedJSX if they wish.

The large ecosystem of component libraries and the backing of a large corporate company is why people continue to use React. That, and that the extra 100kb that comes with React is generally dwarfed by the actual application in any large app.

explaininjs
0 replies
1d1h

I'm using preact for a few projects and the integration with React stuff is generally seamless, but there are enough rough edges encountered that looking back I wouldn't mind taking the difference of a few dozen kB to avoid them all. The vast majority of my load time is taken by TTFB, even from the Cloudflare Pages local cache, so worrying about kilobytes seems misguided.

efdee
1 replies
23h34m

No, jQuery existed for that reason. React has other reasons, one of them being that manual DOM tracking and manipulation is brittle and annoying and the way React works turns that around completely.

I'm not sure what Preact or NakedJSX offers me that would make it a better choice than React.

reactordev
0 replies
23h16m

We don’t do manual dom tracking anymore. We have custom tags, shadow dom, es modules, etc making React obsolete.

snide
0 replies
1d2h

And this is absolutely true in CSS now as well. I find a lot of people are needing to rediscover the standards now that they've upped their featuresets.

hombre_fatal
0 replies
1d2h

Nothing in TFA competes with a web client framework, though, since the framework is solving the general questions of how to manage state and then update the UI when state changes.

For example, pretty much all of the TFA examples are code you'd have to write even if you were using React.

danielvaughn
10 replies
1d3h

So I’m working on this side project. I don’t have a name for it yet but I’ve been describing it as “vim for web designers”. The idea is that you can build a website (or components) in the browser, similar to Webflow, but it’s entirely driven by a command language.

When I began the project, I told myself I wasn’t going to use a framework. After all, I’ve been doing this since 2009, I was working with JS for a long time before I ever touched a framework. And besides, the native DOM APIs have long been good enough. Right?

My god, it was an absolute slog. Marshalling data into and out of the DOM is so tedious and error prone that I began writing my own mini framework to save my own sanity. And then I remembered why I began using frameworks to begin with - because ultimately you’re going to be using a framework of some kind regardless, it’s just whether you want your own custom one or a community-built industry standard.

I do still think native DOM is great, if you’re working with small amounts of interactivity. Most sites that use React probably don’t need it. But if your product has even a little bit of nuance or complexity, I’d go with a framework and avoid the hassle.

In the end I migrated to Svelte, and I’m much happier as a result.

synergy20
2 replies
1d2h

sveltekit is all about SSR these days, svelte itself does not have a default client side router, I was trying svelte for csr spa and gave up

meiraleal
0 replies
1d2h

With the view transition API[1], directory/index.html is/will be the client-side router.

1. https://chipcullen.com/adding-view-transitions-api/

danielvaughn
0 replies
1d2h

I’m using Astro for SSR and only using Svelte for the client-side state. It’s working out well so far.

dmix
2 replies
1d1h

Hyper minimalist projects always end up re-implementing half of jquery or lodash. Much easier to just find a good library that does treeshaking like ramda and use it sparingly.

Klonoar
1 replies
22h5m

I will gladly reimplement half of those projects if it means I can avoid the JS build systems that break when I come back and try to build in a year.

And to be fair I’m sure the build systems have gotten better - they just all left a bad taste in my mouth. I spent more than enough time working with them, writing plug-ins, etc.

danielvaughn
0 replies
21h46m

This was one of the reasons I really tried to go native. Once you introduce a build system, it has to be maintained.

I eventually did choose Svelte, but I’m dockerizing it so I can still use the thing a year from now, unlike virtually all of my other projects.

the__alchemist
1 replies
1d1h

You are building application - I could estimate from your description alone that you're better off wrapping the DOM interactions.

For websites that need interaction, DOM manipulation is IMO a better move due to it using a more fundamental/universal skill, light or no build process, faster execution, and explicitness.

This is a categorization, with lots of room for grey areas! I bring it up because your use case falls squarely in the category that benefits from a framework.

danielvaughn
0 replies
1d

Yeah, agreed. I was hoping I could lean into a quasi-HATEOAS approach, where the state of the editor is reflected in the state of the html output. My thinking was that if it's only in the html, then in theory it would handle very large numbers of nodes better, since you don't have to correspondingly increase the size of JS object in memory.

I'm still attracted to that idea, but it's not worth it in the short term.

bryancoxwell
1 replies
1d2h

How’s that saying go? Choose a framework or you’ll invent one?

danielvaughn
0 replies
1d2h

Yep, and it’s exactly right. It had been so many years since I’d worked directly at the DOM layer that I’d forgotten why I stopped.

That being said, it was equally difficult picking a framework that had the least likelihood to handicap me down the road. And I am still using plenty native methods, especially for the CSSOM. That has yet to be sufficiently abstracted into any kind of framework.

cmrdporcupine
6 replies
1d2h

Nice. Bookmarked. Another good one in a similar vein is https://youmightnotneedjquery.com/

I don't do web stuff often, but when I do I am completely demoralized by the state of framework-itis there. It's gotten completely out of control. A fresh React project is hundreds of dependencies. The weight of that complexity is astounding to me.

Meanwhile the core cross-platform browser tech shipped by default has never been more capable and the amount of code to do common things ... isn't very much.

Front-end devs: are you ok?

danielvaughn
2 replies
1d2h

Agreed it’s out of control. But in the past year I’ve transitioned to a platform team so I’m working with devops-ish tooling for the first time. Holy shit…the amount of complexity there gives front-end a run for its money. You’ve got Kubernetes, Helm, Terraform, CircleCI, yadda yadda. The amount of layered terminology in just those technologies alone is absurd.

throw555chip
1 replies
1d2h

As a mostly backend dev, maybe I'm biased but all the tech you listed there isn't layers of stuff. Each of those do something different. For all but FAANG, kube is overkill. Helm is just templating. The rest existed before DevOps was a term.

cmrdporcupine
0 replies
1d1h

Was replying saying mostly this. I stay away from most of that kind of stuff and work in embedded-type space mostly, but when I have ventured in there, it all kind of makes a certain amount of sense as modules that layer relatively independent of each other. Because, well, most things interoperate via Rest or gRPC or whatever, without a lot of coupling.

The problem with the JS/TS/Node ecosystem is the cultural tendency there to build and proselytize frameworks. Heavily coupling seems to be seen as a positive rather than negative.

troupo
0 replies
1d

Meanwhile the core cross-platform browser tech shipped by default has never been more capable and the amount of code to do common things ... isn't very much.

Have you tried to use this "capable cross-platform tech" to do anything non-trivial? And React isn't the only thing out there.

mattlondon
0 replies
1d2h

I am no fan at all of react or npm, but some frameworks do provide some helpful things that pure DOM APIs do not, so they still have a place I think.

So e.g. application state management and routing are two huge things you'll still need to implement yourself if you just use pure DOM APIs

Depending on the complexity of your app then that might not be such a big deal, but these extras are rapidly worth their weight in gold as soon as you go beyond a basic app. Unless you are using anything that needs NPM in which case you are fucked.

mablopoule
0 replies
1d1h

Another excellent resource in the same vein is the marvelous https://javascript.info/

As a front-end dev who actually like the fundamental platform and the capacities it offers, I'm both happy to see so much nice things coming our way (nested CSS, container queries, and a generally nicer JS experience since ES6) and at the same time horrified by the amount of people using components for trivial things like buttons or bold text.

And I do like framework, but so much framework and library's marketing depend on making people believe that CSS and vanilla DOM manipulation is insanely hard (it's not once you know the fundamentals), and that pulling a random package on NPM is not just quick and easy, but also the professional thing to do.

panzi
4 replies
1d2h

`value.startsWith('javascript:')` and there you have a vulnerability. There can be arbitrary white-space before the URL, so you'd need to do `value.trim().startsWith('javascript:')` instead. However, I much prefer white listing instead, i.e. only allowing `http:`, `https:` and maybe `mailto:`, `ftp:`, `sftp:` and such. Maybe allow relative URLs starting with `/`. Maybe. Though that would mean to correctly handle all attributes that actually can be URLs. Again, I'd just white list a few tags plus a few of their attributes.

simonw
1 replies
1d2h

You mean from this article "Sanitize HTML strings"? https://phuoc.ng/collection/html-dom/sanitize-html-strings/

Yeah, that article really shouldn't imply that sanitization is "that easy". It does at least mention https://github.com/cure53/DOMPurify at the end but it should LOUDLY argue against attempting to write this particular thing yourself and promote that exclusively in my opinion.

Filed an issue about this here: https://github.com/phuocng/html-dom/issues/281

runarberg
0 replies
1d

There is also a future way of doing this with vanilla JavaScript using the Sanitize API. It is enabled under flags in most browsers currently.

https://developer.mozilla.org/en-US/docs/Web/API/HTML_Saniti...

jfhr
1 replies
1d1h

Unless you need to support ancient browsers [1], I'd go the easy way and leave the URL parsing to the browser, e.g. `['http:', 'https:', 'mailto:', 'tel:'].includes(new URL(value, location.origin).protocol)`

[1] https://caniuse.com/url

keepamovin
0 replies
1d1h

JavaScript elegance is always welcome!

bobmaxup
4 replies
1d2h

If I were making a cookbook on HTML/JS I would probably leave out this:

https://phuoc.ng/collection/html-dom/sanitize-html-strings/#...

chrismorgan
1 replies
1d

I’m not sure quite why you’re against removing script tags, but honestly that entire article is poor, riddled with disastrously bad advice:

• “Using regular expressions”: it suggests that this approach is acceptable within its limits. It’s not at all. As a simple example, the expression shown is trivially bypassed by "<script>…</script >". This is why, unlike the post claims claims, using regular expressions for cleaning HTML is not a common approach.

• (“Eliminating the script tags”: I want to grumble about using `[...scriptElements].forEach((s) => s.remove())` instead of `for (const s of scriptElements) { s.remove(); }` or even `Array.prototype.forEach.call(scriptElements, (s) => s.remove())`. Creating an array from that HTMLCollection is just unnecessary and a bad habit.)

• “Removing event handlers”: `value.startsWith('javascript:') || value.startsWith('data:text/html')` is inadequate. Tricks like capitalising and adding whitespace (which the browser will subsequently normalise) in order to bypass such poor checks have been common for decades.

• “Retrieving the sanitized HTML”: you are now vulnerable to mXSS attacks, which undo all your effort.

• “Elements and attributes to remove from the DOM tree”: this proposes a blacklist approach and mentions a few examples of things that should be removed. Each example misses adjacent but equally-important things that should be removed. You will not get acceptable filtering if you start from this approach.

• “Simplifying HTML sanitization with external libraries”: this is pitched merely as easier, faster and cheaper, rather than as the only way to have any confidence in the result.

• “Conclusion”: as I hope I’ve shown, “The DOMParser API is one tool you can use to get the job done right.” is not an acceptable position.

Really, the article could be significantly improved by presenting it as what a common developer might think, and then scribbling all over the problematic things with these explanations of why they’re so bad, and ending with the conclusion “so: just use the DOMPurify library; consider nothing else acceptable”. (There have at times been a couple of other libraries of acceptable quality, but as far as I’m concerned, DOMPurify has long been the one that everyone should use. I note also that this article is talking about client-side filtration. I’m not familiar with the state of the art in server-side HTML sanitisation, where you probably don’t have an actual DOM; this is also a reasonable place to wish to do filtering, but the remaining active mXSS vectors might pose a challenge. I’d want to research carefully before doing anything.)

I look forward to the Sanitizer API <https://wicg.github.io/sanitizer-api/> being completed and deployed, so that DOMPurify can become just a fallback library for older browsers.

bobmaxup
0 replies
1d

I’m not sure quite why you’re against removing script tags

My bad, I left a fragment in the URL.

zeroCalories
0 replies
1d1h

Why? This is a common need. Is there an issue with the implementation or another reason why you would want to use a library?

panzi
0 replies
1d2h

`value.startsWith('javascript:')` and there you have a vulnerability. There can be arbitrary white-space before the URL, so you'd need to do `value.trim().startsWith('javascript:')` instead. However, I much prefer white listing instead, i.e. only allowing `http:`, `https:` and maybe `mailto:`, `ftp:`, `sftp:` and such. Maybe allow relative URLs starting with `/`. Maybe. Though that would mean to correctly handle all attributes that actually can be URLs. Again, I'd just white list a few tags plus a few of their attributes.

alin23
3 replies
1d2h

yeaaaaaaah no. Never going this path again for any interactive page.

This summer I built an irrigation system for my father's field, with the idea that the scheduling would be done in relative time (water this zone for 2h) instead of absolute (start watering this zone at 9AM and end at 11AM).

I kept it as simple as possible:

    - 16 cheap relays for the 24V AC electric valves
    - 1 beefy relay for the pump 
    - 1 Pi Pico W for controlling the relays
Here's a picture of the "low-tech" build: https://files.alinpanaitiu.com/lowtech-irigation.jpeg

The Pico W would have a code.py with all the relay and schedule logic, and an index.html which would be a simple list of sliders to set how much time each valve should be open. The Pico W would function as a "hotspot" which the phone would connect to, and the "app" would be available at 192.168.4.1.

It can be added to homescreen and it looks just like a native app as far as my father is concerned. It doesn't feel like one though, he can tell.

The web page looks like this: https://shots.panaitiu.com/lHMGplFn

That's all there is to it, really. That simple single page with a few sliders and buttons took weeks to build and test thoroughly with only vanilla JS. I was obviously constrained by the flash memory, but there are KB-sized helper frameworks nowadays which I wasn't aware of.

It's also really hard to refactor. I initially stored the schedules at minute-resolution, but then had to store it in second-resolution to add a repeat schedule feature. Oh boy, so much code to parse and change, so many slider.value calculations to redo... yep, never doing this again

topspin
0 replies
22h33m

Is the code shared anywhere? I'm thinking that it could benefit from some input from others that have done a lot of vanilla JavaScript work. That application should be pretty concise and DRY using <template>.

explaininjs
0 replies
1d1h

The magic of react is that it handles the entire View part of MVVC and also provides a nice DSL for writing the View Controller. With a lot of architectural planning you can develop as clean of code without it (cleaner, perhaps), but I agree it's better to just embrace it.

TheRealPomax
0 replies
23h12m

If you want to use the browser as a user interface for an application, use a user interface library. Knowing what you're building is just as important as picking the right tools after you figured that part out: not all web pages (in fact, man web pages) are not, and don't need to be, user interfaces for applications.

purplecats
2 replies
21h15m

what is the value in manually processing knowledge bases like this (besides perhaps in an artisan fashion) if your knowledge interface such as chatgpt already knows it and surfaces it at will?

spacechild1
0 replies
21h13m

You can ask the same question about any knowledge base. Why here? This is completely off topic.

kavaruka
0 replies
21h13m

ChatGPT is useful only if you understand the code that it produces

kossTKR
1 replies
1d2h

Great resource! I've defaulted to just use vanilla js and petite-vue, it's 15 kilobytes and gives a good base.

The problem is the build requirements you run into quickly if you want to use plugins these days with vanilla js.

Elegance was being able to load actual modules from a CDN. Why did we need these anti-web build steps on top of JS?

I feel like there was a sweet spot of complexity around Vue 1 or say 2014.

The Vue 2-->3 jump illustrates the welcome but total breakdown of frameworks in my opinion. It constantly gets in your way and moving data on a page with a hierarchy you need some bizarrely complex data flow, so many folders, files, tools, brittle typescript linting, "auto" this and that, constant building and so many rules it's not fun to engineer anymore.

Anyway, i think we are very close to a sweet spot if we just use something like Petite-vue / Alpine on top of JS maybe with a tiny router and get creative with JS around that, or just early versions say Vue if we need anything more complex.

athanagor2
0 replies
21h57m

The toolchain complexity problem was already present with Vue 2 (and anyway it was usable as a dependency pulled into a classic web page). It definitely could be better, and it seems to be on the way of being improved.

I feel like a lot of criticism toward Vue 3 is based on feelings and vibes. What exactly are your complaints toward v3?

flanbiscuit
1 replies
1d2h

Seems like a helpful list of things to know, I haven't gone through it all but looks good so far.

Clicked on the first one and I noticed something.

They write this:

  const setFavicon = () => {
      const favicon = document.querySelector('link[rel="icon"]');
      favicon.href = (window.matchMedia('(prefers-color-scheme: dark)').matches)
                    ? 'dark-mode-favicon.png'
                    : 'light-mode-favicon.png';
  };

  setFavicon();
And then this:

We can use this function to update the user's color scheme preference whenever they make a change.

  window
    .matchMedia('(prefers-color-scheme: dark)')
    .addEventListener('change', setFavicon);
They are running "window.matchMedia('(prefers-color-scheme: dark)')" twice unnecessarily. If you're going to run `setFavicon` as the callback to the event listener, you can get the result from the `event` parameter passed to the callback like this:

  const setFavicon = (event) => {
      const favicon = document.querySelector('link[rel="icon"]');
      favicon.href = event.matches ? 'dark-mode-favicon.png' : 'light-mode-favicon.png';

This would be my preferred way of doing it because it supports users who are visiting with JS disabled.

  <link href="light-mode-favicon.png" rel="icon" media="(prefers-color-scheme: light)">
  <link href="dark-mode-favicon.png" rel="icon" media="(prefers-color-scheme: dark)">

https://phuoc.ng/collection/html-dom/change-the-favicon-dyna...
explaininjs
0 replies
1d1h

Also, doing it in HTML prevents the flash of alternate-themed content. Doing the same with the `theme-color` meta is also a seldom-used but great feature for detail-minded PWA developers.

bakkoting
1 replies
23h36m

A number of these are using older, awkward APIs, when there's nicer native versions now.

For example, "replace an element" is

    ele.parentNode.replaceChild(newEle, ele);
but unless you're targeting Chrome < 54, FF < 49, or Safari < 10, you can just do

    ele.replaceWith(newEle)
`replaceWith` also lets you replace a node with _multiple_ nodes, or with text.

Similarly, "loop over a nodelist" wants you to use array spread and then forEach, instead of just

    for (let ele of nodelist) ...
akira2501
0 replies
22h59m

Good to keep in mind that nodelist is a live object. So manipulating that list while looping over it can yield surprising results.

wangii
0 replies
1d1h

so, after 10 years of reactjs, we've forgot how to do this in javascript?

tolmasky
0 replies
1d1h

These are great. It's unfortunate that if there's any minor bugs there's no way to really keep in sync though, for example the security issue mentioned in another comment in these comments. It would be cool if these examples could be wrapped up into some sort of convenient "package" or something like that, such that you could easily know if there are any updates and grab them, as well as just make it easier to grab these functions whenever you need them instead of copy-pasting them...

throwaway858
0 replies
22h20m

The technique for "Make a textarea auto-expand" is obsoleted by a new CSS property("form-sizing"):

https://chriscoyier.net/2023/09/29/css-solves-auto-expanding...

Even without using this new CSS property, the technique that is used (adjusting the "height" of the element) is not ideal and can be glitchy.

A better approach is to use a mirror hidden element:

https://css-tricks.com/the-cleanest-trick-for-autogrowing-te...

throw555chip
0 replies
1d1h

AI models will no doubt gobble it up and spit out back out in a unique looking laundered format minus the comments.

sumoboy
0 replies
1d

Same developer who has offers a form validator, very solid and was worth the $ for past projects. Nice list of JS snippets.

selimnairb
0 replies
1d

In Capitalist America, DOM manipulates you!

russellbeattie
0 replies
20h54m

My new favorite bit of vanilla JavaScript has been around since Chrome 1.0 and IE: Element.insertAdjacentHTML. To say it's useful is an understatement, especially with multi-line Strings using `. I can't believe it's been around so long and I only realized it a year or so ago.

The insertAdjacentHTML(position, text) method of the Element interface parses the specified text as HTML or XML and inserts the resulting nodes into the DOM tree at a specified position.

A contrived example of something that's a pain to do with raw DOM:

    let anchors = ['#a', '#b', '#c'];   
    let el = document.querySelector('ul');
    for (let a of anchors) {
      el.insertAdjacentHTML(
        'beforeend',
        `<li><a href="${a}">${a.substr(1)}</a></li>`
      );
    }
Previously I would have to create the li element, then the a element, add it as a child, set the href attribute, then the text value of the a element, then insert it into the ul element. Or, I'd batch it all up as a big string and insert it using innerHTML. Modifying an existing list was even more painful and verbose.

1. https://developer.mozilla.org/en-US/docs/Web/API/Element/ins...

marban
0 replies
1d2h

All good except for Create a custom scrollbar

jeffgreco
0 replies
1d3h

This looks like some helpful refreshers as I try and mess with building a browser extension.

jampekka
0 replies
1d1h

I appreciate the benefit of not having dependencies but not a fan of this rabid vanilla fixation.

Of course you can do anything with Vanilla that you can do with e.g. jquery. Otherwise jquery obviously couldn't do it.

The problem is that the vanilla DOM API sucks. document.getElementById sucks vs $, document.querySelector sucks vs $. setTimeout and setInterval suck. addEventHandler really sucks.

gwbas1c
0 replies
19h7m

FYI: Understanding some very basic DOM manipulation is important even if you are working with very modern tools.

Case in point: I'm working on a shipping Blazor WASM application, and I've had to drop into the DOM twice:

1: We're still using Leaflet.js for a map component. This occasionally requires some DOM calls to adapt between Blazor's worldview and Leaflet's worldview.

2: Blazor WASM loads slowly, so we put up a splash animation in HTML. Removing the splash animation at the "right" time requires DOM manipulation. (Because we want to keep the splash animation up while the Blazor side makes additional calls, so we use DOM manipulation to hide the Blazor-based UI until we've loaded everything.)

fatnoah
0 replies
1d2h

What's old is new again. My first front-end role was building web applications wayyyyy back in 1999. Everything was done with "Vanilla" JS, including fancy stuff like drag and drop in a treeview, click to edit, building what effectively was a single page app, etc. etc. The hardest part was negotiating the differences between Netscape and Internet Explorer.

est
0 replies
1d1h

Is there a CSS version of this? Like Zen-garden but modern

earthboundkid
0 replies
21h17m

https://phuoc.ng/collection/html-dom/resize-an-iframe-to-fit...

There should be a way to do this with CSS and maybe some magic headers. It's super annoying that it has be done via JS.

blablabla123
0 replies
1d

What really blew my mind was when I found out that many of the DOM functions return live HTMLCollections that are automatically updated. I wonder how often that feature is being actively used in production code (before the framework craze or developing with no framework being somewhat more popular again)

beardyw
0 replies
1d2h

I am a great fan of vanilla JavaScript so for me it is a great resource. Sadly many js developers nowadays don't really understand what the Dom can do for them. A great resource!

agumonkey
0 replies
1d1h

Very nice list, it's nice to "down to earth" after too much react ui libs :). Thanks.

Ringz
0 replies
19h59m

Just use HTMX

DerekBickerton
0 replies
23h9m

For syntactic DOM manipulation sugar I use Cash[0]

[0] https://kenwheeler.github.io/cash/