Mini projects built with VanillaJS. No frameworks or libraries
github.com
github.com
You can see it in action here: https://hn-scroll.netlify.com/
The task was to build an offline capable site which displayed the latest Hacker News posts, and loaded more lazily when scrolling. I had to use the official HN API, and I couldn't have any middleware or caching. Everything had to be done client-side.
The app ended up being 1.4kb in total, with support for practically streaming in the items, batching them by network response times and render frames, while keeping the order.
You can see the batched items in this version: https://codesandbox.io/s/hardcore-stonebraker-8m8fx
I wanted to ask, did the company you were interviewing for tell you what they wanted to get out of the assignment on their end - specifically what they were assessing you on?
1) App performance. We will be looking at the wait times for reader to see the content — the shorter, the better.
2) We will evaluate your code’s quality. Does your code have good modular design and testability? Is it easy to read?
3) We prefer the project to be lightweight, and all dependencies should be well justified.
I was focusing on #1 and #3. As for #2, they found it too tightly coupled and hard to read.
I thought that the app is simple enough to not warrant extracting modules in the main app logic. The coupling was also there for performance / filesize reasons.
These sorts of interactions sound pretty far out now, but a few years ago who would have thought that people would be typing code into a web page in real time as part of a job interview process?
Sadly, I didn't get a chance to provide feedback to the criticisms. At least not in an impactful way — the decision has been made.
This was the only time I've experienced that.
It's an arbitrary construct that is not indicative of what form the work will take. Only the first three months of working with someone will give any real indication of what'll be like to work with them for another three+ years.
Agreed.
I had a similar code challenge and after joining the company, in the first year of work I did little if any JS work which even approached the level of the challenge I was presented with. To this day, I'm still bitter the company gave me the old "bait and switch" tactic to get me to come work there.
Adding complexity for performance will be a pure negative unless you can factor that out so the next person can use it on their component.
I would read this code and think I was dealing with a very talented programmer that needs seasoning. Such a programmer can be very dangerous to the wrong company, as the seasoning will have to be added on the company’s dime.
That's a shame. It sounds like the criteria were fairly well defined, but it would be nice if your assignment was used as a starting point for good in person discussion. E.g. "we think the code in index.js was too coupled - what could we do to break that out?"
Out of interest, why did you use display: flex; on the li's and add the list number inside div's?
The browser's implementation of numbering ol's is to align them to the right and render them hanging. They don't impact the layout, much like position: absolute, and they grow to the left.
The default ol/ul behaviour leaves some space to the left where the bullets/numbers should fit. This means you need to take into account the maximum width of the number.
I had no idea how much is the limit of the API, could have been thousands of items, and I didn't want to leave space for four digits until you scroll down to item #1234
I could also have implemented this using CSS `counter-increment` and `content`, but this was a better approach for debugging if the articles were rendering in the right order, while the CSS approach only takes into consideration the amount of DOM nodes.
I think I would have preferred other html elements over the div even with flexbox css, but it's good to hear your reasoning over this.
One thing I noticed though was that creating HTML fragments is a major pain in the ass, which I see you've solved with .createContextualFragment Interesting, I might have to try that one out too.
I myself quite immediately after starting to parse my JSON payload and turning it into dynamic HTML elements turned to Preact, which I guess as a lesser version of React is an acceptable choice. One thing which still was left to irk me were the CSS classes. I'm not a big fan of writing them anymore, with BEM syntax or not. Styled Components in my opinion is the future of writing modular React/web components. But since I vowed to myself to keep this simple, I stuck with CSS.
But it's indeed an interesting project to create a minimal modern website without the massive toolings/libraries you normally take for granted with React etc. There's a lesson there to be learnt in how sometimes that complexity is unnecessary, but sadly it's often not in the scope of a project to start optimizing such things. And I can't blame them, since when things get complicated you much rather have your app already written in React than having to rewrite it from custom JS/TS mess.
https://codyhouse.co/blog/post/css-custom-properties-vs-sass...
I myself have mostly just remained with a one default theme, although adding a dark-theme has crossed my mind several times. It's just that UI features like this are rarely on top of the to-do list.
.gitignore
.prettierrc
README.md
REQUIREMENTS.md
manifest.webmanifest
package.json
tsconfig.json
yarn.lock
Plus the actual source code is spread over four TypeScript files, JavaScript, CSS, and HTML.This isn't meant as criticism of your code specifically but of "modern" web development which hypocritically espouses simplicity but actually increases complexity of development.
My point is your app could have been a single HTML file (or HTML, JavaScript, and CSS if you want to split hairs). Instead there's 15+ files because that's the state of modern development.
And definitely, .gitignore is well known to be responsible for the "complexity of web development." Maybe we should include .gitignore in index.html too?
Just to take that example. Prettier made the state of modern web development so much better.
Unless you're seriously arguing that extra documentation is a bad thing?
> no-framework scripting
This isn't a script. It's a project for a mini app.
> My point is your app could have been a single HTML file (or HTML, JavaScript, and CSS if you want to split hairs)
The app has no bundled deps and no framework.
I actually just went on CodeSandbox, started a vanilla TS project (could've just as easily be JS + jsdoc since both CodeSandbox and VScode are using Monaco, but why would I?). 2 clicks. It uses parcel but I could've just used vanilla JS imports. That's what a few of the other artifacts are from.
Lastly, the app was built as a monolith initially and then I split out a few things to make it more readable.
> .gitignore
bruh.
> README.md
BRUH.
> REQUIREMENTS.md
It's there for the people who need more context
> manifest.webmanifest
It needs offline support. That's how the platform works. Same goes for sw.js
You're being a bit dishonest and writing in bad faith. If you work in web development, you know that the times of inline JS and styles in an HTML file, uploaded via FTP to a LAMP server are long gone. Like, 10+ years gone. If you are still romanticizing that, you may have an advanced case of Old Guard™.
If you are a non-web developer, then I hope you don't apply the same reasoning in your everyday work, for your colleagues' sakes.
I love JS. I love it's ubiquity (F12 in any browser and you've got a REPL). I love the freedom I have when I write in it to do whatever the fuck I want and shoot myself in the foot in all sorts of strange ways. It's a very expressive language that lends itself to all sorts of fun. I love being able to inline C functions as WASM. I love manually managing memory with typed arrays. I love the filth of it all. It's liberating to give up all your dignity and just fling mud with a big grin on your face because your ridiculous 5 lines are a functional standin for a templating engine.
let applyTemplate = template => object => {
let keys = Object.keys(object)
return new Function('input' , `let {${keys.join(',')}} = input
return \`${template}\``)(object)
}The last function called in turn creates a function using the Function constructor, which, since it takes a string argument as the function body, allows the expansion of the values dict into variables and thus the population of the template string:
applyTemplate("aaa ${v}")({"v":"yo"}) --> "aaa yo"
tpl = applyTemplate("Hello ${a}");
tpl({"a": "you"}); // --> Hello you
tpl({"a": "dude"}); // --> Hello dude let applyTemplate = template => object => {
with(object) {
return eval("`"+template+"`");
}
}> Use of the with statement is not recommended, as it may be the source of confusing bugs and compatibility issues. See the "Ambiguity Contra" paragraph in the "Description" section below for details.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
Allowed me to implement TodoMVC in less than 150 LOC. Usage examples start here: https://github.com/stefanhaustein/notemplate/blob/2ee22e392d...
function jstsEngine(string = '', environment = {}) {
return new Function(
...Object.keys(environment),
'output={}',
'return [`' + string + '`, output]'
)(...Object.values(environment))
}
Link with examples: https://github.com/tomhodgins/jsts-engineYou've got an excellent username btw. <3
I'm the developer of Soundslice and continue to be happy with the vanilla JS decision, 8+ years into the company. It means better performance, clearer code, smaller JS filesizes.
I gave a talk about it here: https://www.youtube.com/watch?v=VvOsegaN9Wk ("A framework author's case against frameworks") and here: https://www.youtube.com/watch?v=XH5EtQge_Bg ("How I optimized my JavaScript sheetmusic rendering engine")
You are using fractional units to position it, but you're also using left/top/height properties which can't be fractional.
If you convert these to GPU translated layers via translate, you'll get a smoother playhead since these can be positioned fractionally, plus you'll cut down on the repaints!
Here's an example: https://codesandbox.io/s/magical-wind-mptqn?fontsize=14&hide...
The only gripe I have there is that you need to write imports with a JS extension as the TS compiler won't change them. So to import `foo.ts`, you need to write `"./foo.js"`. The compiler understands it and it also allows the browser to understand it too.
Actually what I really want is to lazy-load modules on demand! So if something is concatenated into a giant file then I don’t need to load modules anymore.
Like maybe this:
function load(module) {
if (load.loaded[module]) return;
import(module)
.then(() => load.loaded[module] = true);
}In fact it's most likely someone has already done that.
find src/**/*.{js,ts} -exec cat {} | typescript >> build.js Q.exports(a, b, c)
Q.require(src, callback)
Following node but just async. It’s simple and it works. We were able to know the name of the latest loaded js file in exports() by a trick of throwing an exception and catching it to see what script was on the top of the stack. All browsers seem to support this.Since it worked back then, we didn’t need anything else. It is super simple to just keep using those things and replace the middleware underneath to whatever is the standard du jour.
It's super short and simple, you can implement similar things in your projects:
https://github.com/Qbix/Platform/blob/master/platform/plugin...
https://github.com/Qbix/Platform/blob/master/platform/plugin...
Here’s a pretty big “VanillaJS” project I built back in 2006, back when we called these things web apps:
(OK, there is a bit of jquery in there.)
I really do hope this kind of developer attitude (writing pure VanillaJS) is coming back.
Also, not everyone wants to maintain a server for everything. Static file serving is just way easier sometimes.
Initially learning a new paradigm (declarative over imperative in this case) is hard, of course, but it can be worthwhile. Whatever works for you though.
Is there a need for these tools all the time? No. But are there valid use cases? Absolutely yes.
rants about having to support IE6
I do have a sense of humor about it. https://jsfiddle.net/gaby_de_wilde/c8bhcatj/
If it's coming, it's coming for the first time.
Way back in the first decade of this century, when JavaScript was first transitioning from a language you wanted to learn as little of as possible to a language you might actually want to learn, jQuery sprang up as a way to deal with browser incompatibilities. I went to a jQuery conference where people were advised to really learn JavaScript and use it where possible, not always relying on jQuery. I don't think that advice was widely heeded.
React took over from jQuery and has the same problem. People learn the library first and slowly pick up the language, hopefully, as time goes on.
I reserve my love only for VanillaJS.
My library for playing with the canvas element[1] is entirely VanillaJS. 0% dependencies. 1000% a learning experience for me, having to seek into the darkest corners of the language to find solutions to problems I didn't even know existed!
[1] Scrawl-canvas - https://github.com/KaliedaRik/Scrawl-canvas/tree/v8-alpha
Sounds like the experience has been beneficial for you, but this excerpt reads like a cautionary tale for using "ValillaJS" for real projects. :-)
I needed a lib to measure text for layout recently, and ran across https://github.com/soulwire/FontMetrics which is nice and clean and completely self contained. It's a perfect library.
npmjs.com shows the number of dependencies at the top but doesn't total in dev-dependencies unless you dive in further. At first glance, npmjs.com leads you to believe webpack has only 24 dependencies when they have 52 additional dev dependencies leading to ~300 transitive deps with ~200 maintainers I now have to either constantly audit or implicitly trust[0]
https://old.reddit.com/r/javascript/comments/9p826q/how_draw...
"Can you point out in this code where using a framework is necessary?"
Never got an answer to that, still people were so nervous about the lack of JS frameworks.
I suspected as much but was always too timid to speak up when I observed how much noise and fury in the office produced so little UI.
There are only 2 APIs in the browser: DOM and the web APIs. As far as writing cohesive logic that does wonderful things that’s on you. Most JS developers would rather it not be on them and instead adopt an Invented Here Syndrome mentality. The fear of writing any original code is very real.
But when I see the mess that front end web dev have to deal with, yes I'm very skeptical !
I find the people that like most of the current tooling grew up with it, don't know any better and don't want to learn.
These aren't web-native so I don't think the comparison is particularly salient. Building on the web is a business requirement, all the tools are to make that requirement manageable.
You can do quite a fair bit with vanilla JS, HTML, and CSS.
Things get a little tricky when you start having to maintain a lot of state that multiple elements need to be aware of.
It's not impossible to do this with vanilla JS, but the mental model can be a bit difficult and this gets amplified across teams.
What crap.js frameworks do well is provide a contract for how state can be organized and how components should react/interact with the change in state. This scales well across teams as following a set of mutually agreed upon conventions produces more legible code instead of an entire team rolling their own code for everything.
For example, if you have a search application and you want to paginate results, you can get pretty far with vanilla js, but you have to evoke changes based on events which is hard to scale beyond one person rolling their own code.
This is a lot nicer with frameworks because you can be less explicit on which elements get updated and rather describe changes in state with elements "subscribing" to these state changes. Furthermore, these frameworks have conventions to follow that makes things consistent which is nice for teams.
I tend to use jQuery for things that require basic JS interactions, but when there are things that require very scoped JS state with a complex set of interactions, I will defer to Vue.js.
Tlbsoftware.github.io/StreamTracer.html
GitHub.com/tlbsoftware/streamtracer
https://github.com/Qbix/Platform/blob/master/platform/plugin...
And the documentation:
https://qbix.com/platform/guide/javascript
It's open source so feel free to rip the code from there, or just use the full Q.js !
For loading code, a long time ago we just implemented two methods:
Q.exports(a, b, c)
Q.require(src, callback)
Following node but just async. It’s simple and it works. We were able to know the name of the latest loaded js file in exports() by a trick of throwing an exception and catching it to see what script was on the top of the stack. All browsers seem to support this.Since it worked back then, we didn’t need anything else. It is super simple to just keep using those things and replace the middleware underneath to whatever is the standard du jour.
It's super short and simple, you can implement similar things in your projects:
https://github.com/Qbix/Platform/blob/master/platform/plugin...
https://github.com/Qbix/Platform/blob/master/platform/plugin...
https://github.com/naikus/svg-gauge This one is all in one widget completely in vanilla js. Zero deps. An interesting thing is there is an angular-js wrapper that someone else built on top of this.
This one is actually a view lifecycle management framework for simple SPAs and mobile web apps https://naikus.github.io/stage/ (This site is actually built using the stage)
As you say though, it's not 100% reliable in terms of speed, but I imagine 90%+ who play the game get a good experience.
[1] https://github.com/bradtraversy/vanillawebprojects/blob/mast...
[2] https://developer.mozilla.org/en-US/docs/Web/API/window/requ...
[3] https://github.com/bradtraversy/vanillawebprojects/blob/mast...
>> The number of callbacks is usually 60 times per second, but will generally match the display refresh rate in most web browsers as per W3C recommendation.
https://dev.to/rishavs/making-a-single-page-app-in-ye-good-o...
If you understand what the framework is doing, used sparingly, some frameworks can save you some time. JS libraries have the same deal, there are some features that I would prefer not rewrite every time I write an app. For instance, PDF.js is quite indispensable at reading PDFs in the browser and I think someone would spend quite a bit of time re-writing all the features you need from that library.
Anyways, I guess it just all comes down to using the right tool for the job.
[1] See https://www.ussherpress.com/synthiejs/ for a simple synth player using web audio or my request-a-drawing site https://www.drommysommy.com
http://aperocky.com/prehistoric
(Mobile not fully supported)
Did use Pixi for sprites and display, but that’s kind of unavoidable.
It was super fun, and here's the result: http://jypepin.com/lolz
A few of those were harder than others and forced me to re-learn some basic maths. I really recommend doing such a thing sometimes!
If anyone wants to learn more vanilla JS stuff, you should check out https://gomakethings.com by Chris Ferdinandi.
https://www.mouse-movement.liammahoney.dev
Heads up it doesn’t work on mobile. Also there some bugs at the end of the game I never got around to ironing out.
The page is 11KB, loads in 96ms, and makes three requests to one network, two of which are favicon-related.
This turns out to actually matter in practice because many of our users are from countries with extremely slow connections.
My point is mostly: do vanillajs but also understand what api's are actually available for you from the start. There is a lot built into the browser, use it. Usually it already has better performance and accessibility than whatever you would write yourself.
[1] https://muki.io
Here's Emoji Tetris in 240 lines. Live demo: https://gist.github.com/mLuby/73316b08c31015e1da813e1fbbed89...
Can also run in NodeJS like so:
$ node tetris.htmlIt runs in-production with 10M+ monthly users!
https://github.com/amark/gun ~13KB
I get a lot of hate for not-using-2ton-framework-of-the-month.
But I think prioritizing performance is worth the flack.
Seeing repos/articles like this deeply warm my heart, maybe us VanillaJS devs are not alone after all!
Does anybody have other favorite VanillaJS projects, that they could share?
That's my aim too. My work is all Vanilla. I hate to use external frameworks and libraries in the browser, but love to use stuff on npm. I prefer to write something I like to use, rather than use something that exists that I don't like working with.
You can see portfolio here: https://github.com/cris691/Portfolio. Bepis and Dumbass are vanilla front-end alternatives to frameworks.
I'm really interested on what you think the hate for not-using-2ton-framework-of-the-month is about?
I'm not sure I understand, it doesn't look like there's any DOM code included. What would you be using a framework for?