10 ms·
Mastering DOM manipulation with vanilla JavaScript
- purplecats 3y agowhat 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?
- kavaruka 3y agoChatGPT is useful only if you understand the code that it produces
- spacechild1 3y agoYou can ask the same question about any knowledge base. Why here? This is completely off topic.
- Tagbert 3y agoWhere does ChatGPT get its pattern to match to produce the output? Likely sources like this.
- snide 3y agoExcellent 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 3y ago> 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 3y agoI 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.
- snide 3y agoAnd 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.
- paradox460 3y agoI even wrote a small article about it at the end of last month: https://pdx.su/blog/2023-10-25-css-is-fun-again https://pdx.su/blog/2023-10-25-css-is-fun-again
- irrational 3y agoI though React, et al were originally created as a better way to manage DOM updates (shadow DOM). Has that need gone away?
- evan_ 3y agoReact 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
- 3y ago
- hombre_fatal 3y agoNothing 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.
- jeffgreco 3y agoThis looks like some helpful refreshers as I try and mess with building a browser extension.
- mg 3y agoThese days, I get by with just two dependencies. One is dqs.js: https://github.com/no-gravity/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/ https://github.com/handlebars-lang/handlebars.js/ Which I use for all my frontend templating needs.
- dylan604 3y agoare 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 3y agoWhat'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 https://news.ycombinator.com/hn.js
- evbogue 3y agohn would benefit from the ael library
- throw555chip 3y agoThat'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.
- deleted 3y ago[deleted]
- dylan604 3y agoI'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.
- danielvaughn 3y agoSo 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.
- bryancoxwell 3y agoHow’s that saying go? Choose a framework or you’ll invent one?
- danielvaughn 3y agoYep, 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.
- synergy20 3y agosveltekit 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
- marban 3y agoAll good except for Create a custom scrollbar
- beardyw 3y agoI 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!
- bobmaxup 3y agoIf I were making a cookbook on HTML/JS I would probably leave out this: https://phuoc.ng/collection/html-dom/sanitize-html-strings/#eliminating-the-script-tags https://phuoc.ng/collection/html-dom/sanitize-html-strings/#...
- panzi 3y ago`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.
- zeroCalories 3y agoWhy? This is a common need. Is there an issue with the implementation or another reason why you would want to use a library?
- deleted 3y ago[deleted]
- chrismorgan 3y agoI’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/ https://wicg.github.io/sanitizer-api/> being completed and deployed, so that DOMPurify can become just a fallback library for older browsers.
- cmrdporcupine 3y agoNice. Bookmarked. Another good one in a similar vein is https://youmightnotneedjquery.com/ 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 3y agoAgreed 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 3y agoAs 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 3y agoWas 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.
- mattlondon 3y ago
- deleted 3y ago[deleted]
- deleted 3y ago[deleted]
- deleted 3y ago[deleted]
- panzi 3y ago`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 3y agoYou mean from this article "Sanitize HTML strings"? https://phuoc.ng/collection/html-dom/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 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 https://github.com/phuocng/html-dom/issues/281
- runarberg 3y agoThere 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_Sanitizer_API https://developer.mozilla.org/en-US/docs/Web/API/HTML_Saniti...
- panzi 3y agoYes, that one seems promising, but I really would like more control on allowed URLs. I.e. a flag to allow/disallow cross domain URLs anywhere (<a href>, <link href>, <form action>, <img src>, <img srcset>, <iframe src> etc. etc.) And does it strip any styles (style attribute, style tag, link stylesheet)? If yes, can I allow certain styles somehow? Needs work IMO.
- jfhr 3y ago
- fatnoah 3y agoWhat'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.
- aatd86 3y agoHow was the state and its mutations managed? Why does it seem that we don't know how to do these things anymore? ;]
- fatnoah 3y agoIn general, the goal was to manage as little state as possible, so anything that was a one-off change, such as changing a single field, changing a node parent, etc. resulted in an async server round trip. This was done to reduce JS complexity (no jQuery or equivalent, so we had to maintain separate code for IE and Netscape) and to ensure validation was consistent. For more form-oriented activities, we used a more traditional form post & page refresh. At the time, XML was the hotness, so all of the server back end APIs accepted and responded with XML, so when we did actually want to manage state, we did it through modification of the XML representation of the data the user was working with.
- kossTKR 3y agoGreat 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 3y agoThe 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 3y agoSeems 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-dynamically-based-on-user-color-scheme-preference/ https://phuoc.ng/collection/html-dom/change-the-favicon-dyna...
- explaininjs 3y agoAlso, 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.
- alin23 3y agoyeaaaaaaah 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 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 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
- explaininjs 3y agoThe 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 3y agoIf 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.
- throw555chip 3y agoAI models will no doubt gobble it up and spit out back out in a unique looking laundered format minus the comments.
- est 3y agoIs there a CSS version of this? Like Zen-garden but modern
- jampekka 3y agoI 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.
- deleted 3y ago[deleted]
- tolmasky 3y agoThese 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...
- T3RMINATED 3y ago[dead]
- wangii 3y agoso, after 10 years of reactjs, we've forgot how to do this in javascript?
- agumonkey 3y agoVery nice list, it's nice to "down to earth" after too much react ui libs :). Thanks.
- blablabla123 3y agoWhat 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)
- avidphantasm 3y agoIn Capitalist America, DOM manipulates you!
- sumoboy 3y agoSame developer who has offers a form validator, very solid and was worth the $ for past projects. Nice list of JS snippets.
- bakkoting 3y agoA 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 3y agoGood to keep in mind that nodelist is a live object. So manipulating that list while looping over it can yield surprising results.
- Kailhus 3y agoOr “is an element within viewport”, we now have IntersectionObserver for that sort of stuff. I guess it could be combined with the rects but if I remember correctly, the logic would fail if the element was bigger than the viewport.
- DerekBickerton 3y agoFor syntactic DOM manipulation sugar I use Cash[0] [0] https://kenwheeler.github.io/cash/ https://kenwheeler.github.io/cash/
- throwaway858 3y agoThe 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-textareas-probably-eventually/ 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-textareas/ https://css-tricks.com/the-cleanest-trick-for-autogrowing-te...
- earthboundkid 3y agohttps://phuoc.ng/collection/html-dom/resize-an-iframe-to-fit-its-content/ 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.
- russellbeattie 3y agoMy 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/insertAdjacentHTML https://developer.mozilla.org/en-US/docs/Web/API/Element/ins...
- Ringz 3y agoJust use HTMX
- gwbas1c 3y agoFYI: 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.)