Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
A little bit of plain JavaScript can do a lot (jvns.ca)
397 points by ingve on June 21, 2020 | hide | past | favorite | 198 comments


I agree about the nuisance of creating DOM elements. innerHTML is OK if you’re doing static content, but for anything that needs to be dynamic (untrusted input, event handlers, etc.) I have a little tiny helper library that I carry around in my head and write into projects that need it:

    const $T = text => document.createTextNode(text)
    
    function $E(tag, props, kids) {
        const elem = document.createElement(tag)
        for (const k in props) {
            elem[k] = props[k]
        }
        for (const kid of kids) {
            elem.appendChild(kid)
        }
        return elem
    }
(Add flourishes as necessary for things like optional arguments and allowing falsey kids.)

This isn’t as nice as JSX, but it makes a reasonably ergonomic API for quick little projects. Here’s what the example in the article would look like (admittedly it’s a bit easier to follow with syntax highlighting):

    const button = $E('button', {}, [
        $E('i', {className: 'icon-lightbulb'}, []),
        $T('I learned something!'),
        $E('object', {data: '/confetti.svg', width: 30, height: 30}, []),
    ]);


A bit more minified/modern version of this that I'm using:

    function $e(t='div',p={},c=[]){
      let el=document.createElement(t);
      Object.assign(el,p);
      el.append(...c);
      return el;
    }
    
    var $t=document.createTextNode.bind(document);
That's 173 bytes not minified, might be useful for someone.

Interestingly, the function names are exactly the same - I guess people think similarly :-)


I like how short yours is—I really must use Object.assign() more. I hadn’t heard about append(); since my original version supported IE it had to use appendChild() in a loop and I never realized there was a better option.

It looks like append() also accepts strings directly. This eliminates the need for $T, which makes things even nicer.


Isn't clarity better than bytes. We can always running through a minifier at build time.


A minifier is better, but I find that snippet of code clear enough for personal use (el is element, t is tag, p is props, and c is children).

The small bytes also mean that for simple sites I can just copy paste it before transitioning to a real setup.


Clarity is only useful when the variable scope is complicated, which it isn’t here. The problem COULD be that the ultra-short variable names make an unclear external API… but given the function is internal, the context of its use would make it rather obvious it’s constructing HTML elements.

You’re not wrong about clarity; it wouldn’t hurt here at all, but it wouldn’t hinder without, so I imagine it’s to reduce cognitive load and make it abundantly clear the function is logicless glue.


Instead of the loop, you can just use: Object.assign(elem, props). I found I wanted a more data-driven style that matched the element types themselves, so I use the somewhat more cumbersome:

        // data driven HTMLElement creation                                                                                                                     
        var $element = function(type, p={}) {
                let h, elem = document.createElement(type);

                if (!p || (typeof(p) !== "object")) {
                        elem.innerHTML = p || '';
                        return(elem);
                }

                h = p.attributes; delete p.attributes; if (h) for (let e of Object.entries(h)) { elem.setAttribute(e[0],e[1]) }
                h = p.classList;  delete p.classList;  if (h) for (let c of h) { elem.classList.add(c) }
                h = p.dataset;    delete p.dataset;    if (h) Object.assign(elem.dataset, h);
                h = p.style;      delete p.style;      if (h) Object.assign(elem.style,   h);
                h = p.innerHTML;  delete p.innerHTML;  if (h) elem.innerHTML = h;
                h = p.children;   delete p.children;   if (h) for (let ch of h) { if (ch) elem.appendChild(ch) }
                h = p.parentNode; delete p.parentNode; if (h) h.appendChild(elem);
                h = p.event;      delete p.event;      if (h) for (let e of Object.entries(h)) { elem.addEventListener(e[0],e[1]) }

                return(Object.assign(elem, p));
        };


Using innerHTML as default if the second argument isn't an object risks XSS vulnerabilities. I'd prefer innerText for as default.


What XSS vulnerability? Any user can set the innerHTML of any element at any time.


XSS is when other users can write javascript that executes on your machine, like if they can set their forum signature to `<script>fetch('/send/@attacker/100usd')</script>` and the client uses innerHTML to render it on your machine in the context of your authenticated session.


> What XSS vulnerability? Any user can set the innerHTML of any element at any time.

This mindset right here is exactly why XSS is still an issue.

If you pull user generated content and put it in the DOM like this, you will open your users to XSS from other users. Basing your personal use DOM APIs on setting `el.innerHTML` will lead to a slip-up. Use `textContent` by default.


Oh my, of course. My assumption was that this function would not be used with dynamic, possibly user-defined input. This is small-scale thinking, and obviously if you intend for this code to be reused then this case must be accounted for. I'm pretty terrified that I was able to look at this code and have that assumption, even though I know better. I've even caught and resolved a couple XSS vulnerabilities at companies I've worked for. What does this say about me? Maybe another question to ask is, what does this say about the value of a web framework?


> I'm pretty terrified that I was able to look at this code and have that assumption, even though I know better. I've even caught and resolved a couple XSS vulnerabilities at companies I've worked for. What does this say about me? Maybe another question to ask is, what does this say about the value of a web framework?

I don't think it says anything about you, it's a very easy mistake to make. But it should say something to you, which is to stay vigilant and to try very hard to not dismiss security concerns without giving them some thought.

And of course always assume code will be misused if you let it, by others who don't know what you're going for and by yourself when you're trying to make a deadline. So always design your interfaces to be secure by default. Obviously easier said than done...


Often times you write code to defend against other developers who don't know any better.


Most of the time I write code to defend against future me, because I know that in a few days I'll have forgotten half of the code along with all the optimization hacks in it.


The problem is that the code $element("span", text) looks harmless, appears to work, and yet is dangerously wrong.

Dynamically setting text is very common. While dynamically setting innerHTML is a rare and dangerous operation which should be explicit in the code. The alternative syntax also supported by this function $element("span", { innerHTML: html }) is much better.


https://portswigger.net/web-security/cross-site-scripting/st...

You protect against someone else abusing it on other users.


Neat solution - out of curiosity though, why remove the properties from the passed options? If I called that function, I wouldn't normally expect it to mutate my arguments like that.


At a glance, they do it so that at the end they can merge in any remaining props they didn't handle after all their work building `elem` is done.

This can be accomplished non-destructively with destructuring.

    const {
      attributes,
      classList,
      style,
      ...rest
    } = p

    // elem = ...consume attributes, classList, style...

    return Object.assign(elem, rest)


This approach with destructuring is great but you will need to transpile the code if you're targeting IE11 which is still often the case in enterprise settings unfortunately.


Others have already mentioned that you can replace "for (const k in props) elem[k] = props[k]" with "Object.assign(elem, props)"; further to that, in modern browsers, you can replace "for (const kid of kids) elem.appendChild(kid)" with just "elem.append(...kids)" - with the added benefit that plain strings passed to .append() get turned into text nodes automatically, so you probably wouldn't need $T any more:

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


Yeah, this evolved in my head from an early version that had to support IE, so it turns out there’s a bunch of cruft in it that I hadn’t realized wasn’t necessary any more. pcr910303 posted a version which uses the same APIs you mentioned, and I think I’m going to use that one in the future.



Not nearly as minimal as your example, but I like the "no build tools route" of using preact. You don't need to build your code, and you can get JSX-esque syntax and some of the niceness of React without messing with npm or webpack or any of that.

https://preactjs.com/guide/v10/getting-started#no-build-tool...


You used to be able to do that with React, too. I think I still got an app running that uses the JSX transpiler you loaded in a <script> tag...


I am failing to understand why someone would choose this over React. The page you linked to, I kid you not, has a section about how you build a Preact application from the command line...


That is the getting started page. It lists multiple ways to get started. One of them is simply including the pre-built preact library with a <script> tag. After that you can use h() to build elements - no build step required.

I personally use preact over react because it's tiny - 3kb for the entire library.


I linked to the "no build tools route":

> Preact is packaged to be used directly in the browser, and doesn't require any build or tools


Oh I gotcha! That quote applies to react as well though (I've done it many times!).

I suppose I could rephrase my question to something like "What is the point of this library?". Every other page on that site that includes examples uses JSX... which means there needs to be a build step.

I guess if someone really just wants to include a 3rd party library to programatically create elements, but get none of the benefits of using them this would be great! Then again there is like 3 examples in this thread of tiny functions (< 10 LoC) that appear to do the same thing.

I guess 3.5kb is about 10x smaller than React + React DOM, but 35kb isn't exactly breaking the bank either. I can't tell specifically by glancing over their examples, but I also suspect feature parity is not quite there. Specifically, I develop with Typescript so being able to type hint `React.ChangeEvent<HTMLInputElement>` etc. is sometimes necessary. It's not clear if Preact exposes a sufficient API. Maybe you know?


> I suppose I could rephrase my question to something like "What is the point of this library?"

It's just a lightweight, no-build-steps-required library that's similar to React. If you already like using React there's nothing much compelling to make you switch.

I like using it with HTM for a nice alternative to JSX which doesn't need any build steps.

I can't speak on its interaction with TypeScript, I've only used this for simple pages where I want to add some interactivity without involving the rest of the nightmarish JS ecosystem.

https://github.com/developit/htm


Because React tries to do so many things nowadays, I can no longer justify the bundle size cost when I can have Preact do what I need at a fraction of the size.


$E is pretty close to React.createElement, which JSX's tags compile to

https://reactjs.org/docs/react-without-jsx.html


I’m not surprised by the convergent evolution—it’s the obvious API for creating elements. (My earliest versions of $E had arguments (tag, kids, fn) and would call fn(elem) so it could perform arbitrary modifications on the node, but I eventually realized that all I ever did was set properties and it was silly to have an entire lambda just for that.)


Using tagged template literals the way lit-html does is the nicest JSX-substitute I’ve seen: https://github.com/Polymer/lit-html


It's pretty nice but doesn't play so nicely with editor indenting modes and stuff like that, so there are some reasons to use normal JavaScript function calls instead.


In emacs, with evil mode, I can do:

    <esc>vi`:edit-indirect-region<ret>:html-mode
And edit the string contents as HTML.


There are plugins for many editors that give syntax highlighting, code completion, type-checking, etc. The experience is great.


The downside there is you’re heavily relying on strings, which feels a bit weird for things like event handlers, which would either have to inline the function as a string or do some magic behind the scenes. The editor is also going to be less helpful in figuring out your intent when using a string-only templating system.


I don’t think it’s actually strings: the tag gets the value of the expression in ${}s and can return whatever sort of object it wants. As I understand it, it basically creates HtmlTemplate objects or some other kind of Fragment: the strings are only there for specifying the tags and static attributes.

See “tagged templates” here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...


lit-html is not a "string only" template system. Because tagged literals can contain JavaScript expressions we process many other data types and handle them appropriately.

For event handlers, only the event name is in the string. The handler function is passed directly in and we add it to elements with addEventListener().

The only "magic" is that we wrap the user's handler call it with the host component as the `this` value so that you don't have to create closures like in Reach. You can just do:

    class MyElement extends LitElement {
      render() {
        return html`<button @click=${this._onClick}`>Click Ma</button>
      }
      _onClick(e) {
        console.log('this is', this);
      }
    }


And here we are in 2020 with everyone writing their own poorly documented and tested partial version jQuery. Instead of everybody working on the same better, faster, probably already cached library.

When will we ever get that jQuery’s thing wasn’t only cross-browser compatibility. It’s that the DOM’s native API is terrible and jQuery is a much terser, powerful, chainable alternative.


To not need many custom helper functions like this I still prefer to throw in jQuery for "small" more serverside oriented projects. It gives a well documented and standardized API.


You may be interested in re:dom then. It does no “vdom”, “mount/components” part is optional, and its functionality is aligned with $E, with some extensions. I did not use it in my projects yet, but it worked pretty well in experiments.

https://redom.js.org/#elements


I also have custom DOM helpers I carry around with me:

* getNodesByType - allows me to get things like attributes, text, and comments directly

* getAncestor - allows me to get a specified element somewhere between a target element and the document.documentElement


getAncestor is built-in: element.closest() :)


Slow. It uses a selector as an argument, which requires a parse step. It doesn’t take much effort to write something substantially better.


Tiny templating libraries like mustache are ideal for that. https://mustache.github.io/


Could never get over Mustache using {{foo}} for safe auto-escaped interpolation but {{{foo}}} for dangerous non-escaped interpolation.

I wonder how many XSS vulns this decision has caused in the wild.


I'd say that's the more sensible decision if you want to maintain syntax.

You can grep for {{{ without false positives matching {{. Conversely, it's harder to find places where == was used when === should have been used (although I'll concede it's not much harder). In general I'd prefer to go the extra mile to be unsafe than accidentally miss something out.


Maybe my point wasn't clear, but {{{ and {{ look too similar. It should be {{ and something else. Like {{Dangerous=username}}.

You shouldn't have to squint at your templating code to see if there's an XSS vector or not, or defensively/neurotically grep for "{{{" just in case you didn't trust your team to squint sufficiently.

For comparison, here's JSX: <div dangerouslySetInnerHTML={{__html: username}} />.

vs. Mustache: {{{username}}} in a file of 1000 other { and } glyphs.

Mustache's hey-dey is long over thankfully.


I agree that something possibly dangerous should be harder to mis-use. The world is full of small oversights. Having said that, surely this is the job of a linter - does one exist for mustache?

I've never seen JSX before (I haven't learned anything new in front-end later than ES5). In your example, it looks more like an attribute than an inner tag, the former of which I'd almost always escape. Is that how you set inner HTML as well?


Funny to see because I do the exact same thing down to the parameter names, almost token-by-token identical. I usually just name the function "tag" though.


Would you mind explaining what this does?


Its just a helper function to create an element, assign it some properties, and append children elements. Not very much unlike React in the API (emphasis on "interface") but of course the inner workings is as minimal as possible.


Could you explain how it does this?


I don't know much js, but it seems fairly straightforward. Here's some comments:

    // create an element with 'tag'
    const elem = document.createElement(tag)

    // copy properties into the new element one at a time
    for (const k in props) {
        elem[k] = props[k]
    }

    // append child elements one at a time
    for (const kid of kids) {
        elem.appendChild(kid)
    }

    // and that's it
    return elem
Applied recursively, you can make dom elements with very little boilerplate.


> I have a little tiny helper library that I carry around in my head and write into projects that need it

May I suggest a repo. Github is great, I hear. :)


I’ve never worked as a professional frontend developer, so even though I’ve been writing HTML/CSS/JS for 15 years for little side projects, all of the projects have been pretty small

I'm pretty much the same, and one thing I've noticed which continues to both amuse and sadden me is the fact that those whose main focus is not web development often make better sites/pages than "professional" developers. I once rewrote, in a few hours, an internal site that had been developed by another team over a few weeks but with many problems (it was an enormous SPA), and made it so much faster and easier to use that once my coworkers found out, they all switched to using mine. That didn't go over well with the team who built the original...

Another experience I've had with a dedicated frontend developer: https://news.ycombinator.com/item?id=18486124


I think I know why that might be the case: they used an approach appropriate for large-scale projects in a small-scale project - happens very often really.

None of the max 30-user SPAs I wrote during my time at a certain large pharmaceutical company made sense as SPAs, because the scale at which that becomes beneficial just wasn't there.

As for your other experience: I get where you both are coming from. Thing is, we as front-end developers are kind of forced to use that guy's approach, because over the years stakeholders got used to a level of flexibility that's just not achievable using yours.

I for one would love to use minimal amounts of JS instead of frameworks that make me wait 2 minutes on my sad little work laptop until they compile the project, but that would make any event in which some stakeholder changes their mind a disaster requiring me to re-write the whole application every now and then.

Anyway there's light at the end of this tunnel in the form of compiler-frameworks:

https://svelte.dev/

Writing in this feels just like old school vanilla JS, but with additional language features like reactivity.


I believe a lot of it is also because many genuinely useful features from frameworks have recently been added to the core language. "Vanilla JS" (and "Vanilla CSS") in latest-generation browsers with support for JS modules, JS classes, async/await, fetch, querySelectorAll, Flexbox and Grid Layout is something else than writing "Vanilla JS" five years ago.


The only way your comparison works is if your point is "Framework driven apps are never good". I've lost count of the number of times I've pointed out that a good vanilla JS is always going to be better than a bad framework driven app. One is a good app and the other is a bad app. It's obvious, and therefore not a useful comparison.

The valid, useful test is whether or not a good vanilla JS app can be better than a good framework driven app.


Yes, but the choice of approach isn't outcome-neutral.

There is also the question of whether going-vanilla more often results in good apps.

I'd go so far as to say the "naive approach" for most things, whenever it delivers on all the requirements, is always the best approach.

Be it relational (vs., nosql); grep vs elasticsearch; etc.


The naive approach if you're a developer who knows React is to use React for everything.


> The valid, useful test is whether or not a good vanilla JS app can be better than a good framework driven app.

A much more useful test is to look at the average vanilla and framework app. Obviously the best framework app or vanilla app is gunna be pretty good regardless.

Do frameworks encourage bad/lazy coding? Do they make it very easy to half baked something but very hard to create something polished? Does the constant churn of frameworks mean that a project can never be mature and stable?

Is plain JS too restrictive? Do you have to reimplement react yourself if you want to make a good website?

I think these are the relevant questions.


I'm sorry to say this, but it's not a problem with the newfangled stuff. I think you just don't work with very good frontend devs. Which isn't surprising honestly - it has the lowest barrier for entry of any programming specialization (I started there myself).


I think its a problem with selling. People want to sell. So out with the old, in with the new. Lets revive some of the trends from the 70's. Or lets rewrite all this code to use classes. Sell more books and courses. Hate on all solutions that dont require out products. Are you still writing plain old JS? In order to be pro you need to use these frameworks... Its all marketing. Trying to argue is like standing on a train track and argue a train to stop. The train will just continue. And plenty of people wanting to be on the hype train.

Second problem is that buyers are not result focused. Few companies understand that they pay for these cool frameworks every time a user visit their website. They pay with 15% less conversion, or lower user satisfaction.


Or they have real value by helping developers deliver features that users love while contending with the inherent statefulness of clients.


If you're a front-end only developer not only do you use the same hammer (big framework) for all nails, the use of that hammer is what gives you job security and satisfaction. If companies switched to using old fashioned server-rendered pages with light JS, half of HN would not have anything to do at work.


For the first year, anyway. If the project continues to grow, next year they would be called to deal with the enormous spaghetti mess that has resulted.


It’s as if everyone forgot why we moved presentation logic to the same place and why we favor declarative programming over DOM mutations. When you render is completely orthogonal to that.


right, i only do vanilla: sites are finished fast and they just work. not someone any one would hire. people are really used to frameworks. it would be a step back for them.

>In one place I wanted to change a button’s HTML contents.

if you know what the hidden elm is going to contain you can just add the html to the page as a hidden elm. if it can have 2 states you can also add both nodes as html. just show only one at a time.

the <i> and the <object> are always children of <button> i would target them as such, no need to have a class. unless you use <i> 2x. (one for each state) but i think the icon should be the button bg.

onclick gives you a reference to the button with which you can change it's class. things inside (now styled by the classname of their parent) will change along. (swap bg, hide/show etc)


I can build out a backend with a microframework and sqlite in no time. Why doesn't every company just throw out all the frameworks (or frameworks disguised as libraries) and just do what I did?

Because it doesn't scale for large projects and is impossible to work on with larger teams. Rails may seem bloated to some people, but if you are creating a set of complex business rules that constantly change and cost too much to rewrite with the framework flavor of the day, it makes a lot of sense.


I'm an experienced React developer.

I wanted to try out writing a plain vanilla JavaScript application - I enjoy plain JavaScript, it feels close to the metal.

It wasn't long before I was craving an application framework that allowed me to cleanly organise and structure my application instead of it rapidly becoming a spaghetti.

I also craved the ability to write small simple functions for making components.

And I wanted a sensible way to organise data flow through the application.

And I really, really wanted to be able to write in a declarative programming style instead of imperative - this is something you don't come to appreciate fully until you've lived it.

And I don't care what anyone else says - I love JSX - it's totally makes sense to me.

So despite a solid effort to try to go to vanilla JavaScript I've decided that a really good JavaScript application framework - whatever one you like - is a good thing. I'll leave vanilla JavaScript to the birds.


> I enjoy plain JavaScript, it feels close to the metal

Never thought I'd hear the day...


> it feels close to the metal.

Please tell me you're joking?


I mean he is a React developer after all, less than 3 levels of abstraction is greased lightning.


As a React developer I have the strictest policy to never go lower level than the browser and I never touch other fields of computing. I started my computing career in 1986 knowing only React and I'll get to the end of it knowing nothing else.


Damn, you must be the one scooping up all those 10+ years React experience jobs :)


I’m giving GP credit for the joke :) it’s hard to mistype 201X as 1986.


This gotta be some kind of mistake. :) React 1986?!


>> it feels close to the metal

> never go lower level than the browser

So maybe "close to the DOM" rather than "close to the metal"? :-)

On the other hand, if you ever want a project to get you out of your comfort-zone(/rut?) - check this out:

https://www.espruino.com/

You _can_ go "bare metal" and still use your hard won javascript skills...

> I started my computing career in 1986 knowing only React

That's a big gap in your memory between 1986 and when React was first released in 2013. Or _maybe_ 2010 or 2011 if you were working inside Facebook back before they released it publicly...


> That's a big gap in your memory between 1986 and when React was first released in 2013. Or _maybe_ 2010 or 2011 if you were working inside Facebook back before they released it publicly...

You do realize why you're being downvoted, right? :D


Cmon. Close the the "browser metal".

I of course don't mean close to the CPU that would be silly.


"Close to the Chrome"



The web will be very close to metal in five years. Google is working on it.


You can have a nicely organized JS project without using a framework, albeit you can expect a bit more boilerplate.

There are many approaches you can take. The following is an MVC approach I'm trying out in a personal project. It's organized as follows:

There's a subscribe/emit system, where you can link functions to run when an object sends an emit signal. This can be done in a couple dozen lines of code. Example: https://gist.github.com/stefano/32cbe9bef5a68ecb3286113369d8...

Models are JavaScript objects. They have methods to read their state and to update it. The methods that update the state need to call emit() so all subscribers are notified.

Views are functions. They return an object with 2 properties: a DOM node (the root of the view), and an update function. The update function takes a model instance, and updates the DOM structure accordingly. If a view supports user interaction, it'll take a delegate object as a parameter. The view will add event handlers which call the delegate object. Example of a text input with a label and optional validation error: https://gist.github.com/stefano/2135cc15470cceba4ac2d344fe2e...

The controller is a function or class. It returns an object with its root view and a function to run when the controller is destroyed for cleanup. It can either take a model as a parameter or instantiate it. It also instantiates the root view, passing itself (or another object) as the delegate. In the delegate methods, it calls the model update methods. The controller subscribes the view to model changes, and unsubscribes them when it's destroyed. Alternatively, the view can subscribe/unsubscribe itself.

With this approach, views can be composed by calling the view functions from inside another view, passing down a delegate and adding the root DOM node to the parent view DOM structure.

The controller and the model are completely independent of the DOM. The model can be tested independently. The controller can be tested with a mock view. The view can be tested by passing in a mock model.

Views only depend on their initial state and the state of the model passed to the update function.


The trick is to make as much as possible static. Just pure functions. Second trick is to have only one way direction, eg components only listen to events, they don't emit events, then they don't have to depend on each other, which in turn will lead to better reusability and less spaghetti.


Pretty much this. I knew all of the things she wrote about but all I could think was "I can't imagine writing foo.classList.add and remove 100 times. That's going to create some major spaghetti code once that project grows beyond a couple of pages"


There;s some deep truth here, and I suspect it's about a fundamental difference between "writing a plain vanilla JavaScript application" and "I wanted to use the same HTML to generate both a PDF (with Prince) and to make an interactive version of the questions."

Reimplementing gmail or slack in html/css/vanilla-javascript, without an "application frameowrk is no doubt an exercise in futility.

At the same time, reaching for React/Fluttr/Angular/frontend-framework-de-jour when all you need is to show and hide divs and maybe do css or scroll animations between them is, in my mind, an equally bad decision.

(I can totally see why the framework-de-jour option gets chosen so often though - because no matter how written-in-stone the original specs saying "There wil be no functinality required beyond showing/hiding divs and css animations between them" are, we've all seen projects where a last minute critical/showstopper requirement for the web page to include something insane like a fully functional email client gets dropped in the FE dev's lap the Friday afternoon before launch Monday. Sure, the FE dev made the choice to use React, and while some (perhaps a lot?) of that is "resume driven development", at least some of the blame is on bad sales people and project managers as well...)


> reaching for React/Fluttr/Angular/frontend-framework-de-jour when all you need is to show and hide divs and maybe do css or scroll animations between them is, in my mind, an equally bad decision.

Do people really do that? To me, that reflects bad frontend engineering practice. Even so for something like that, I'd use jQuery or something similar, because it's a much nicer API than the DOM.


How do you generate html in the first place? With React you have the same UI logic in one uniform framework. Whether you render statically, on request or in the client doesn’t split up your code. Everything plays together nicely. You get sensible editor support with linting and static analysis and more comprehensive error messages.


Why bother with jQuery? I don't see the value add anymore.


Read this whole thread. People are rewriting it over and over again because it’s out of fashion.


>Reimplementing gmail or slack in html/css/vanilla-javascript, without an "application frameowrk is no doubt an exercise in futility.

I'm pretty sure gmail started out without a framework.


> I'm pretty sure gmail started out without a framework.

I'm pretty sure you're right.

I'm also pretty sure it's not only one of the big reasons frameworks exists as they do today (because it's perhaps one of the earliest and probably the most famous example of a web app that 100% _needs_ a framework for all the reasons discussed everywhere in this thread), but that it's also the poster child case of why every ambitious/opinionated/inexperienced FE dev builds their own framework (Of _course_ I can't use jQuery/Angular/React/Fluttr! _My_ ToDo list app is _groundbreaking and special_ - just like gmail was in 2004, I'll need to write my own framework for this. Stand back! <flexes>") ;-)


Isn't that where Angular comes from?


And now we witness everyday spaghetti code written on the top of some framework and each noodle is wrapped five times in a needless abstraction.

There is nothing preventing you to write nice and clean code in plain JS.


Yeah no shit. JS is the only community I've seen where code size scales with dependency count in the positive direction. It's ridiculous.


Kudos on pushing yourself beyond your comfort zone. I think you were still thinking in terms of React though, with statements like "I also craved the ability to write small simple functions for making components". Maybe explore a template based approach instead, with the help of a library like Mustache.js so that you're not overwhelmed by having to do it all the first time.


It seems like WebAssembly will allow high level languages to get close to the "browser metal", so I think we will see declarative frameworks that are closer to the metal compared to React. I'm wondering if Rust is the best language to learn to get a headstart on that.


I had the same experience recently. I love using lightweight web apps. But I much prefer writing with React-like technology. Inferno is a great React alternative, FYI that keeps the app a little lighter while still providing a sane developer experience.


When starting to learn/write JS, I found a few of these basic name-shortening functions from HN's own JS very useful, and well-named:

    function $(id) { return document.getElementById(id); }
    function byClass (el, cl) { return el ? el.getElementsByClassName(cl) : [] }
    function byTag (el, tg) { return el ? el.getElementsByTagName(tg) : [] }
    function allof (cl) { return byClass(document, cl) }
    function hasClass (el, cl) { var a = el.className.split(' '); return afind(cl, a) }
    function addClass (el, cl) { if (el) { var a = el.className.split(' '); if (!afind(cl, a)) { a.unshift(cl); el.className = a.join(' ')}} }
    function remClass (el, cl) { if (el) { var a = el.className.split(' '); arem(a, cl); el.className = a.join(' ') } }
    function html (el) { return el ? el.innerHTML : null; }
    function attr (el, name) { return el.getAttribute(name) }
    function tonum (x) { var n = parseFloat(x); return isNaN(n) ? null : n }
    function remEl (el) { el.parentNode.removeChild(el) }
    function posf (f, a) { for (var i=0; i < a.length; i++) { if (f(a[i])) return i; } return -1; }
    function apos (x, a) { return (typeof x == 'function') ? posf(x,a) : Array.prototype.indexOf.call(a,x) }
    function afind (x, a) { var i = apos(x, a); return (i >= 0) ? a[i] : null; }
    function acut (a, m, n) { return Array.prototype.slice.call(a, m, n) }
    function aeach (fn, a) { return Array.prototype.forEach.call(a, fn) }
    function arem (a, x) { var i = apos(x, a); if (i >= 0) { a.splice(i, 1); } return a; }
    function alast (a) { return a[a.length - 1] }
    function vis(el, on) { if (el) { (on ? remClass : addClass)(el, 'nosee') } }
    function noshow (el) { addClass(el, 'noshow') }
    function elShow (el) { remClass(el, 'noshow') }
    function ind (el) { return (byTag(el, 'img')[0] || {}).width }
https://news.ycombinator.com/hn.js


You might be interested in bling.js: https://gist.github.com/paulirish/12fb951a8b893a454b32

I was similarly inspired:

    if (undefined === window.$) {
        window.$ = document.querySelectorAll.bind(document);
        [EventTarget.prototype, window, XMLHttpRequest.prototype].forEach(function setOnOff(p) {
            Object.defineProperty(p, "on", {
                get() {
                    return function onElement(t, f) {
                        this.addEventListener(t, f);
                        return this;
                    };
                }
            })
            Object.defineProperty(p, "off", {
                get() {
                    return function offElement(t, f) {
                        this.removeEventListener(t, f);
                        return this;
                    };
                }
            });
        });
        [NodeList.prototype, HTMLCollection.prototype].forEach(function setOnArray(p) {
            Object.setPrototypeOf(p, Array.prototype);
            Object.defineProperty(p, "on", {
                get() {
                    return function onArray(t, f) {
                        this.forEach(function onEach(e) {
                            e.addEventListener(t, f);
                        });
                        return this;
                    };
                }
            });
            Object.defineProperty(p, "off", {
                get() {
                    return function offArray(t, f) {
                        this.forEach(function offEach(e) {
                            e.removeEventListener(t, f);
                        });
                        return this;
                    };
                }
            });
        });
    }


The class related functions have been made native by the classList property of elements.

    element.classList.has('someClass') //true if present
    element.classList.add('someClass') //add to element
    element.classList.remove('someClass') //remove from element
    element.classList.toggle('someClass') //remove if present, add if not present
I would also argue element.querySelector and element.querySelectorAll are plenty shorthand to replace the selector helpers!

Not saying HN should rewrite their JS obviously, just for anyone reading this and wondering if there are native equivalents.


querySelectorAll is a lot slower than old school functions. You would be tempted to say who cares, but then you load Gmail and wait 2 seconds for UI to render.


Do you have any references for this? I would be flabbergasted if the simple case of #id and .class weren't optimized to be basically identical. (the only difference being that they had to do a quick "parse" before jumping into the optimized path)


https://jsperf.com/getelementbyid-vs-queryselector/25

tldr just looking for class x10 slower.


Trying that on Firefox mobile and they are all within 5%. The direct calls are faster but that could conceivably be the time to check for special cases with a DOM that small.


https://www.sitepoint.com/optimizing-css-id-selectors-and-ot...

The slowest and most convoluted selector took 21ms when tested against 50,000 elements using a 2014 MacBook Pro.


20ms for one call over 50000 elements, on a 2.6GHz cpu capable of 4 IPC. 50-200K instructions per element. Truly we are living in the future :)


Thanks you! I was hoping someone would spell out the "changes from the last 5 years" mentioned elsewhere.



> well-named

They look useful to me, but I personally think they aren't so well-named; excessively concise for my taste.


Fair enough. Your comment (which I upvoted) reminded me of this, from Emerson's journals:

Wm Little came to church & heard my sermon against minding trifles. He told me, had he preached he should have taken the other side. Probably not one hearer besides thought so far on the subject.


Nice work! The only advice I’d give is don’t use patterns like

    div.parentElement.parentElement.foo()
It’s inflexible and will break if you ever make dom changes. Use the .closest() selector instead. It’s like a reverse .querySelector (.closest selects ancestors, .querySelector selects descendents) which means you’ll have more specific code.


TIL about .closest!

From the article.


It’s a little funny seeing vue/angular/react/whatever people discover vanilla JS. I thought classList, innerHTML, and querySelectorAll were all extremely common knowledge.


If I'm not mistaken, she's a systems programmer. More low-level stuff. She's definitely not a frontend/React/vue.js dev, as she says in the article.

She's written some excellent articles on lower-level programming.

But I agree that VanillaJS is not appreciated by a large number of web developers.


That's because in order to build anything of any remote sophistication, you need a framework, otherwise you are committing to maintaining an unmaintainable spaghetti mess. Just like nobody actually built Windows applications with just the Windows API -- they all used frameworks like MFC or reimplemented those frameworks in-house.


> in order to build anything of any remote sophistication, you need a framework

Sure. But does the project/requirement described in the original article sound particularly sophisticated?

I note that HN loads less that 150 lines of js and no framework. While there's an argument to be made that this place's frontend code is "not remotely sophisticated", it without doubt provides a huge amount of value anyway.

While I agree with everybody here about the requirements for good frameworks (and good development practices) for complex or sophisticated web applications, to me at least it's abundantly clear the author of the article made the correct choice for her problem domain.

Some things should resist every effort to make them more complex or more sophisticated than they need to be. Most things probably.

"Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away." -- Antoine de Saint-Exupery

I strongly believe that, while he did not know it at the time, he was totally talking there about lines of code.

Bill Atkinson clearly took that philosophy to heart:

"He was just putting the finishing touches on the optimization when it was time to fill out the management form for the first time. When he got to the lines of code part, he thought about it for a second, and then wrote in the number: -2000."

from: https://www.folklore.org/StoryView.py?story=Negative_2000_Li...


The issue is that on regular projects/products, you are trying to hone on perfection by adding and removing a bunch of things and trying different stuff. This repeats for a longer period of time, say 6 months to a year.

The codebase has got to be able to survive that churn in a sensible way, and certain tools like (ES6) modules, types and declarative UI descriptions that automatically update to reflect the changes really help with this process.

Why does this declarative approach help? Because the more features you add, the more places can generate events that cause updates to the same DOM, and the number of possible transitions between those DOM states grows a lot larger. If the number of possible states that the DOM can end up in is N, the number of possible transitions is N^2.

To a first approximation, declarative frameworks scale linearly with the number of interacting features, while imperative (the vanilla DOM API) scale quadratically.


I don't disagree, but I see so many usages of React that are mostly static websites and sit in front of an existing MVC framework server side. I think a little bit of native JS can accomplish a great deal of the requirements for most front end work, and when it gets too complicated for native you'll know. What I would advocate against is starting with React, because then your lowest possible tooling complexity is React and it's build pipeline.


I totally agree. Frameworks are needed for large projects. You either use an open source one, or build one yourself. And usually, the former is greatly preferred.

However, if you're a frontend dev, you should understand VanillaJS. Just like a systems programmer should understand assembly and C. Also, there are some situations where you won't need a framework.

Knowing VanillaJS and using frameworks are obviously not mutually exclusive.


I just launched a commercial strategy game with a fair chunk of (DOM-based) UI elements and wrote everything in raw JS.


Except for the stuff everything else runs on top of like operating systems, servers, runtimes and browsers.


Of course you can do quite a bit with JS. Some of the projects I’ve made

You can swap CSS classes in JS. But how do you know which classes are available?

You can target HTML elements in JS. But how do you know which elements are available?

You can set “.innerHTML”. But how do you save the state of the application?

As soon as the app grows a little bit in complexity, you’ll end up building functions to keep track of things and functions to perform repeated actions. You end up building a tiny framework that only you know how to use. When it’s time to debug and grow, you’ll need a proper JS framework anyway.

The thing I would say about popular JS frameworks, and which I’m surprised about, is how they favour single page app structures rather than “drop-ins” to enhance a page. I’ve yet to see React or Angular be used as part of a page, which is crazy because they would enhance the developer experience so much, without disturbing the classic way of delivering pages server-side with classic routing and classic user experience.


There are other frameworks that do favour a "drop in" style of development, with native Web Components being one of them. I used Riot for this for a while, and tried native Web Components as well.

My current approach for zero dependency/build, one off UI components is:

    <div class="some-component" data-props="{}">
        ...more HTML...
        <button class="some-component__button">Click Me</button>
        <script type=text/javascript">
            (function() {
                var component = document.currentScript.parentNode;
                var props = component.dataset.props;
                var button = component.querySelector('.some-component__button');
                 
                button.addEventListener('click', function(){
                    ...doStuff...
                });
             })();
         </script>
    </div>
If it needs a global state, or is part of a larger more dynamic UI I'll start looking at a framework. But for an otherwise static website, it's so much easier to do it that way than it is to introduce an entire framework and build pipeline in order to do a handful of JS components. It's also a somewhat defensive approach, useful in legacy applications with lots of hodge podge JS going on. Generally speaking, you don't cause any problems, and don't encounter any conflicts.

I used to have a class based/web components way to do this, but I don't even bother with that anymore. You don't gain anything from introducing complexity in this circumstance.


> You can swap CSS classes in JS. But how do you know which classes are available?

> You can target HTML elements in JS. But how do you know which elements are available?

This assumes someone else is creating the CSS and HTML for you? Have rules or coding and naming conventions like BEM for example. That's what a framework basically is, a set of rules and conventions about how to structure an App. What you need is proper architecture, regardless of whether or not it is based on a popular framework. That's how the popular frameworks started out to begin with.


If someone uses framework because they would write spaghetti code without it, I guarantee that they still write spaghetti code, just served in the framework provided boxes.


Indeed! I swore off Web development a couple times because of the mess but now my whole party games platform is made with plain JS, no bundling, no post-processing, no big frameworks and it's pretty good. (Although I did recently augment it with some JSDoc types, for long-term maintainability)

At least three devs have asked why I wasn't using some reactive framework or big library. I know why: I'm much more productive without! Simple DOM helpers go a long way: https://jklm.fun/common/dom.js

(If you're curious: https://jklm.fun)


Well isn’t that the economics of it? A new dev is much more productive in the new thing they learned. A more seasoned dev is more productive in all the things they already know.

The question is, who gets to dictate the economics here? The answer is always Thanos, balance.

In a true meritocracy this is a legitimate fight, neither side has a one up. We have to find the pros and cons of the old and new and iterate.


@op Nice!

Might be good time to also mention these sites:

http://microjs.com/

http://youmightnotneedjquery.com/



This is another great resource for vanillajs snippets https://htmldom.dev/


More specifically, how JavaScript can do a lot in browser by interacting through the DOM API.

The modernization of the DOM API in the last 5 years has done a lot to remove the need for jQuery et al, and has made building the View part of JavaScript apps much more frictionless.


Where can we learn about this “modern JS“? I feel like in some ways I’m learning the old ways, similar to old c++.


The parent isn't talking about JS: they're talking about the DOM APIs. The best resource I've found for learning about these is MDN: https://developer.mozilla.org/en-US/docs/Web/API/Document_Ob.... I've found this to be an incredibly helpful reference when I need to do something without using a library.


Following the tc39 proposals on GitHub is a great way to keep up with features just being added. "What's new in ES2020" type blog posts should catch you up between then and the bleeding edge. MDN for actually getting the technical details on things as you try to implement the features you read about (will also xover browser specific APIs). caniuse to see if they are actually ready to be used.


It's both modern Web APIs as well as ES6 JavaScript, both of which make working in VanillaJS quite pleasant.

If you don't need to support older browsers, you don't even need to use any packaging software (although if you're building a serious app, it's still wise to do so). You can import ES6 modules in vanilla JS, and a modern browser will do the loading for you. Great for quick starts.


You still end up re-implementing half of jQuery. Because element creation is just as much passion as it was in 1999. Because useful functions are limited or stunted compared to jQuery counterparts (querySelectorAll returns a weird object instead of an array and throws exceptions if you as as much as as look at it funny, etc.).


Either iterate through the `NodeList` with a `for ... of` loop or a `.forEach` method, or convert it to an array using `Array.from()` or `[...nodeList]`.

`NodeList` can be a live list (not `querySelectorAll` though) which has some benefits over arrays.

`NodeList` also implements `Symbol.iterator` so you can use your favorite iterator library if you need to map, filter or reduce it. And with the future pipeline operator, you’ll really don’t see a different between a standard array and a fancy structure like `NodeList`.


[flagged]


Well, you called it a "weird object" that throws errors if you "look at it funny." They tried to demystify it for you in just three sentences.

That jQuery lets you willfully cling to the corpse of familiarity instead of spending 30 seconds looking up what NodeList was years ago says more about you than anything about jQuery.

And with your end-cap comment about the pipeline operator that they were already, helpfully, pointing out was going to be introduced in the future, you just sound like sour grapes. What gives? Kind of a weird attitude to bring to a forum of craftspeople.


> Well, you called it a "weird object" that throws errors if you "look at it funny." They tried to demystify it for you in just three sentences.

I never said it was mysterious to me. I described it as concisely as possible. It is an object that has a grand total of two array-like methods (and it took the standards bodies two years to add forEach to it). And it does throw an exception on invalid input.

So, to work with it without hassle, guess what, you'll have to recreate that "corpse of familiarity" from jQuery: throw an Array.from at it, and wrap it in a try-catch.

And that is true for every single improvement to the DOM API. If you look at all the efforts to get rid of jQuery, you'll see people re-implementing half of it for one simple reason: all the improvements are still stunted, underdesigned, and need quite a lot of additional boilerplate to make it useable in any hut the simplest scenarios. (Notable exception: classList. It weirdly behaves and works the way that doesn't screw up developer experience).

> And with your end-cap comment about the pipeline operator that they were already, helpfully, pointing out was going to be introduced in the future, you just sound like sour grapes. What gives?

The pipeline operator proposal has been there for three years. Excuse me for not jumping with joy when someone quips that there will be no difference between arrays and NodeLists in some glorious future. I prefer reality.


ha, i just went through this today, querySelectorAll returns a NodeList, which is static rather than live, requiring you to iterate through the list rather than acting on the elements directly.

but it's nice that jquery (as convenient as it is) is no longer a must-have library for dom manipulation.


An interesting use of 'classList' is that it makes it easy to use css-classes to indicate the state of a dom-element. Is the element selected or not? Well does its classlist include the css-class 'selected'?


Do not use classes to mark something as selected. People using screen readers won't know whether something is selected. Use aria attributes like aria-selected instead.

https://www.w3.org/WAI/PF/aria/states_and_properties#aria-se...


This is a good approach as it helps you solve a lot within CSS. Want to show the subpages of a selected menu point? Just use a css selector which changes their display state.


If little can do a lot just imagine what I can do with few megabytes: Google Gmail/Youtube engineers


Probably a good batch of spaghetti code if you think vanilla can be used for anything serious.

Or rewrite react yourself as another commenter already did.


It is an old meme, that every js-developer, who does not use an existing framework, ends up with his own framework. (also happened to me)

Depending on the scope of the work, this is not allways a bad thing. I like my own framework. It does exactly what I want and I can tweak it to my needs anytime.

But I work alone and don't have to share my code with different developers, otherwise there would be problems ...


Yeah, the one complaint with frameworks is they never do exactly what you want, instead forcing you to do what generally works for everyone, which is usually good enough. But also can be frustrating. Which is probably why there are many frameworks.


So, question for those who are following the JS ecosystem trends more closely: Is innerHTML now officially "ok to use" again?

I remember way back (when XHTML was still on the table) that innerHTML was effectively deprecated: It was still around but you weren't supposed to use it for anything new because support might be dropped at any point in "the future". It was also non-standard. Instead, you were supposed to use the DOM APIs (createElement etc) for any kind of DOM modifications. (Not even talking about the risk of performance issues or XSS vulnerabilities if you used innerHTML naively)

Now "the future" has come around and innerHTML is still supported basically everywhere. It's clear that implementing innerHTML correctly adds substantial complexity to browsers (they have to parse and serialise arbitrary HTML fragments on the fly, synchronously, without stalling the event loop. The result also has to interact well with all the other DOM manipulation/inspection APIs. Not to mention if the HTML string contains its own bits of JS...) - however, there seems to be so much "legacy" content that dropping the API seems infeasible.

So, does anyone know what the official state of innerHTML is in terms of standard compliance and future support?


Whether or not it is widely supported is academic - you shouldn't use `innerHTML` for the same reasons you shouldn't use `eval`, the most important being that it is a security risk.


> I remember way back (when XHTML was still on the table) that innerHTML was effectively deprecated

I had heard that document.write is discouraged but not innerHTML. In fact, browsers have doubled down, by adding insertAdjacentHTML (originally only in Internet Explorer).

> Instead, you were supposed to use the DOM APIs

I tried that in an app. It turned out that IE 6 was faster with innerHTML, like literally 1,000 times faster. This was surprising because I assumed the DOM APIs were closer to the metal. Not so, at least with Internet Explorer. (With Chrome, the DOM APIs and innerHTML were about the same speed).

> standard compliance and future support

It will be supported forever, because browsers refuse to break the web. You can see them saying so when they discuss syntax for new features.

Mozilla's documentation tells you when a feature is deprecated (again, usually for a feature that was experimental and never widespread). It carries no such notice on its page for innerHTML. It does, however, carry a warning: "Warning: If your project is one that will undergo any form of security review, using innerHTML most likely will result in your code being rejected." --- https://developer.mozilla.org/en-US/docs/Web/API/Element/inn...

To me, innerHTML is sometimes useful and no more dangerous than server-side rendering. Rather than deprecating it, I wonder why browser vendors have not added a native escapeHTML function (or even better, a very short syntax) to make innerHTML safer.


The DOM draft [1] seems to include it. Legacy content is better slow than unusable, however, so it’s better off being discouraged – by linters, dev docs and a warning in the console. FWIW, this is currently linted in TSlint but not JSlint.

It would be nice to have this (via regular or template strings) compile to DOM objects, wrapped with a couple of pointers indicating where strings or elements are placed. I guess for now the best course would be to budge that into transpilation, raising an error if it does anything kooky like work at the boundaries of elements.

(.innerHTML setting is sugar for a HTML parser being initialised on the element, so such a transpiler wouldn’t be removing functionality per se)

[1] https://w3c.github.io/DOM-Parsing/#dom-innerhtml-innerhtml


Don’t use innerHTML with data that you got from a user, directly or indirectly. But if it’s all your data, it’s great.


I’ve been doing web development for more than 23 years. I’ve pretty much stuck to vanilla JS that entire time (though I did use jquery during the worse of the browser incompatibility years). I keep meaning to learn some of the newer libraries but never get around to it since vanilla JS does everything I want it to do and I already know it. I wonder if I would be as surprised by what libraries like Vue can do as this person is about vanilla JS.


Not a Vue example but have a look at https://github.com/drcmda/reactanoid

It's an Arkanoid clone (bat and ball game) written in React, with react-three-fibre for WebGL, zustand for state management, and use-cannon for physics. The full version is about 250 lines and there's also a simplified version that weighs in at 60 lines (not including the library code obviously). It runs at a solid 60FPS on a very basic laptop.

Doing the same thing in vanilla JS, using the same libraries (or alternatives) would be a lot more work, and would end up with the exact same result. That's the point with a well written framework-driven app - when you use something like React or Vue you should be aiming to get the same end result as a vanilla JS app, but with much less effort. The cost to the user should really only be a slightly bigger download.


Never set .innerHTML ! document.createElement is very tedious. But you can hide them inside pure functions that returns your component. You could for example put the confetti button example into a function and whenever you want a confetti buttton const button = confettiButton("hurray"); body.appendChild(button); Or you could make a class and extend the Button into a confetti button.

You can then use innerText to change values. And use other DOM methods instead of redoing the innetHTML. When using function abstractions the DOM updates can be private, and you don't have to worry about innerHTML elsewhere overwriting your changes.

Basically React was invented to overcome spaghetti innerHTML, but you can solve the problem with vanilla JS too, just stop thinking about the DOM as HTML, think of it as a tree.


I enjoyed this. It conveyed a little of the sense of wonder that can come with just a touch of JS without bashing the frameworks. I also liked the emphasis on making HTML and CSS do the heavy lifting by adding and removing CSS classes as triggers.


Instead of HTML, can’t JavaScript just be used to paint the browser canvas?

You can create your text boxes, your drop downs, buttons, etc., everything that makes it a GUI application.

Then you fetch your data, per the page you display, via JSON, and fill in the fields.

The initial JavaScript download is heavy, but the normal usage of the web application should be quicker, as you’re only fetching the relevant data to fill each of the required widgets.

This does sound resource intensive, for computers back in the 1990s, but today’s iPad, iPhone, and modern laptop computers should be powerful enough to handle it.

Or did I just describe some already popular JavaScript framework?


> You can create your text boxes, your drop downs, buttons, etc., everything that makes it a GUI application.

The browser gives you all that for free, just by writing some HTML. And allows your website to be accessible and indexable. It seems like a lot of work to re-implement that, for little benefit. It doesn't seem worth the hassle, but maybe there's something I've overlooked.

> normal usage of the web application should be quicker, as you’re only fetching the relevant data to fill each of the required widgets...Or did I just describe some already popular JavaScript framework?

Every popular JS framework already does all of this.


I think you just described the browser. While there are some efforts to do canvas-only rendering in JS, they aren't mature and are meant for specific use-cases like game boards and stock charts. For a typical website, this approach would likely have negative implications for your site's accessibility and usability; the site would also be harder to index.


That’s correct. I was thinking more along the lines of video games and interactive stock charts. But as an easier way to develop GUI applications via JavaScript.

Perhaps where the widget definitions are described via an easier, LISP like syntax, and rendered by the Framework. Kind of like Emacs Lisp.

Actually, it might have been amazing if the original Netscape browser shipped with a Emacs Lisp like scripting tool to begin with. But instead, we got JavaScript.


> Instead of HTML, can’t JavaScript just be used to paint the browser canvas?

Yes it can. Qt when compiled to web-assembly does this. It just uses a canvas as a framebuffer basically. [0]

Going off on a tangent here. This is something I've been thinking about a lot lately. I wanted to build a cross platform app that also works in the browser. Especially as single developer maintaining multiple codebases just sucks. So the only real option seems to be to write it as a webapp. So I started, but personally, I just really can't stand the JS ecosystem.

I basically see two ways here this could go. Either the we go the use-canvas-as-framebuffer route. Or we'll need small embeddable html/css engines so you can write your code once in your preferred language, where for native apps you access the engines DOM directly, and when compiled to WASM it 'syscall's it out to JS see e.g. go [1]

Basically something like sciter[2].

[0] https://www.qt.io/web-assembly-example-slate

[1] https://www.godoc.org/syscall/js

[2] https://sciter.com/


Interesting, this is more in line with what I was thinking of.

But, JavaScript is such a poor language, that it’s a poor choice for rigorous software development. However, it is a good candidate for transpiling code to.

I was thinking of bolting on an Lisp type of scripting language, which can transpile down to a rigorous subset of JavaScript, which then controls how the widgets behave in the browser.

This way, the Lisp script can be used to control how the widgets are organized in the browser screen. How each widget gets its data, and how it interacts with other widgets on the screen. Basically, it becomes its own little GUI development environment.

Then over time, a public library of Lisp functions can be made available, and easily integrated into your code, with recommendations on commonly accepted techniques to do something. But if you don’t like that, then you’re still free to reinvent your own new technique.

Then over time, the superior nature of this system, becomes popular enough that the major browser vendors begin integrating the Lisp scripting engine as a first class library. And to improve efficiency, it can cut out JavaScript entirely, and just interact directly with the browser. So the Lisp engine should have the ability to both, transpile down to JavaScript for backwards compatibility, as well as to compile down to bytecodes that can be executed by the browser. This way, all the variant JavaScript ecosystems gets neutralized, since this new system, just sidesteps JavaScript completely. (Wait, I think I just described WebAssembly.)

And then, a new variant of web-Lisp takes over the world.

Excellent..


There is a large semantic gap between those two. Transpiled languages are either very similar to the target, or lose important parts of functionality (or run in an in-target-written vm, which is not a viable option in general).


"The initial JavaScript download is heavy"

Is 64 kb heavy?

There is a graphic libary, that (partly) tries to accomplish this. (but did not really take off)

lib.ivank.net/

Basically the idea was to replace the DOM with a canvas and then create elements in it, like you did in flash.

If it would have matured, you probably could have then used Flex, which is a flash libary, similar to the DOM, but much cleaner. (but flex is heavy, though)

But apparently, no one thought it is worth it. Because, it would mean quite some work, to reimplement the DOM in a faster way ...


You described an SPA. Fetch relevant data and render to controls. Not sure why we need canvas for that.


You are correct. I just inadvertently re-invented React.


React is a view engine and isn't using the canvas.

React pretty much is just a fast way to determine what to re-render on a page as a result of a state change.


> Instead of HTML, can’t JavaScript just be used to paint the browser canvas?

Flutter does this for web support.


Thank you for this. Really. Thank you.

I wish we had more postings like this.

No complaining, no whining, no "my framework is better than your framework". Just what problems you had and how you solved them.

This actually gives me something to take away.


This is great. In legacy codebases for my employer I'll always reach for vanilla JS when solving a problem or implementing a new feature. Better than tacking something on in jQuery.


After a full year of only working with React, I started working on a few projects a couple of days ago with Vanilla JS only and it is refreshing. It feels like writing Haiku poems.


Unfortunately the reason we have frameworks is because there are so many gotchas. It's true - vanilla JS can do a lot however, there are also a lot of strange edge cases that aren't handled well. If you don't mind failing on these cases then it is fine, but this is why things like jQuery were built and why your React project has hundreds of dependencies. Personally I prefer vanilla JS, but for any serious project it is hard to choose it


Does anyone have a nice list of interesting, compact examples of sites or apps that are written in vanilla javascript?


I think if you make websites at any layer you owe it to yourself to learn fundamental CSS and modern JavaScript (ES6).


What sort of automated test framework do folks use while writing vanilla JS?


I use AVA[1] and c8[2] for coverage. It only works in node so I have to use JSDOM to mimic the browser environment.

1: https://github.com/avajs/ava

2: https://github.com/bcoe/c8


If I need tests I usually reach for the same tools I’d use for testing any other JS—in my case mocha, chai, and nyc—plus jsdom and jsdom-global (so I don’t have to spin up a browser for unit tests) and chai-dom.


In 2020, with our mature and cross browser compatible JavaScript, it surprises me that many people are still discovering vanilla JavaScript. Is something wrong here?


I get the sense that there are a ton more beginner tutorials based on frameworks. And those type of tutorials are underrated ways that lots of people use to learn "js".


A little bit of C can do a lot, yet most 3A games are written with frameworks.


If all you need to do is display a native dialog, read some files, etc. you're better off without the massive 300MB game engine. A lot of people making native apps aren't game devs and a lot that are aren't making tripple-A games.

In the same way there's definitely web apps that are better off with a large framework, but if all you're doing is toggling some classes you're better off without them. Most websites don't need to be SPAs.


It's as if using the technology the way it's designed to be used is a reasonable idea. Who could've thought?


This is super super minor but it's JavaScript not Javascript. IMO the camel case makes it look so much better :D


If we're nitpicking, it's Pascal case when it starts capitalized :-p


I'd paint that bikeshed UpperCamelCase.


If you really want to nitpick, it's ECMAScript


Well, once it had been LiveScript…


If you really want to be technical, it's Self in Java garb.


It should be javaScript


Here's what a "little bit" of JavaScript can't do:

  1. Manage subtle browser differences
  2. Store and manage reactive state
  3. Manage declarative updates
You can build a simplified version of React in under 100 lines of code, but that probably won't take care of subtle browser differences. A little bit of JavaScript can do a lot, but it can't do everything you need these days.


I don’t think the author is arguing that you have to replace your React applications with vanilla JS, but instead of using React by default, you could consider using vanilla JS.

The tree things you mentioned might be important to you for a specific project, but these are not requirements for every project. So instead, do what makes sense to you. And remember that you don’t always need a framework to achieve what you actually need.


Does anyone know a 100 line react clone or tutorial? That sounds very educational.





Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: