DEV Community

loading...
visuellverstehen

Use components without a frontend framework

Malte Riechmann
Software developer at visuellverstehen
・3 min read

Frontend frameworks

Within this post, I will show you one, of many ways to use components without a frontend framework. Do not get me wrong, I like frameworks like Vue.js, React, or Angular. At our company, we write JavaScript with Vue.js on a daily basis.

But sometimes those frameworks are too much. Especially when building simple websites instead of complex web applications. In these cases, we do not use a framework at all and just write HTML, CSS, and JavaScript.

Components

If your source code gets messy there is often an easy way to improve it. Move your source code into smaller chunks – divide and conquer.

Splitting your source code into multiple components is a well-known way to structure your project. Things get isolated, readable, clear, reusable, extendable, and maintainable. In fact, that is what most of the frontend frameworks do themselves.

Welcome to BEM

BEM (Block Element Modifier) is a methodology to organize your frontend. It is mostly known in the world of CSS, but I am going to show you how to use it for JavaScript, too. Please remember, BEM is a methodology and not a framework. It will come with zero dependencies.

A card block

In BEM the components are called blocks. It is just different terminology for the same thing.

Now, imagine a card block (source code).

Example of a card block

HTML

We use HTML for the structure and content of the card block. Of course, you can use programming languages, template engines, or other tools to improve the HTML part of the block.

<article class="card card--highlight">
    <figure class="card__figure">
        <img class="card__image" src="#">
        <figcaption class="card__caption">#ffed00</figcaption>
    </figure>
    <h2 class="card__headline">Corporate yellow</h2>
    <p class="card__description">This yellow is defined as #ffed00. It is a very nice color. It is one of our corporate colors at visuellverstehen.</p>
</article>
Enter fullscreen mode Exit fullscreen mode

CSS

We use CSS for the presentation and style of the card block. Of course, you can use pre processors, post processors, or other tools to make CSS more comfortable to use.

.card {
  font-family: sans-serif;
  line-height: 1.5;
  padding: 1rem;
  max-width: 20rem;
  border: 0.25rem solid #f6f6f6;
}

.card.card--highlight {
  border-color: #ffed00;
}

.card__figure {
  margin: 0;
}

.card__image {
  display: block;
  width: 100%;
  height: 6rem;
}

.card__caption {
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}

.card__headline {
  font-size: 1.5rem;
  font-weight: bold;
}
Enter fullscreen mode Exit fullscreen mode

JavaScript

We use JavaScript for the functionality of the card block. Of course, you can use all kinds of tools to improve how you write JavaScript (Babel, TypeScript, ESLint, webpack, …).

(function () {

  var initializeCard = function($card) {
    console.log('Do whatever this $card block should be doing.');
  };

  document.addEventListener('DOMContentLoaded', function() {
    var $cards = document.querySelectorAll('.card');

    for (var i = $cards.length - 1; i >= 0; i--) {
      initializeCard($cards[i]);
    }
  });

}());
Enter fullscreen mode Exit fullscreen mode

Sometimes blocks have to communicate with each other. For that, there are at least two good options: Custom events and stage management.

Learn the basics

It is remarkable how much can be done just using HTML, CSS, and JavaScript. That is one reason why I encourage everyone to learn the basics.

Discussion (17)

Collapse
zzoukk profile image
zZouKk • Edited

I read 3 times and unless I’m blind, I don’t see where you applied BEM in javascript.

Also the $ sign here has nothing to do with jQuery I guess.. just a naming convention to point out that it holds an element.

Collapse
malteriechmann profile image
Malte Riechmann Author • Edited

Thanks for your comment. It made me smile. You are not blind. You are right. I did not go into details with applying BEM to JavaScript, although I said so: »I am going to show you how to use it for JavaScript, too«.

The JavaScript part in my post does show how to create an isolated environment for each instance of a block. To go further, maybe a small teaser for a gallery block can help.

(function () {

  var initializeGallery = function($gallery) {
    var $galleryNextButton = $gallery.querySelector('.gallery__next-button'),
      $galleryPreviousButton = $gallery.querySelector('.gallery__previous-button'),
      $galleryItems = $gallery.querySelector('.gallery__items');

    $galleryNextButton.addEventListener('click', function() {
      // …
    });

    $galleryPreviousButton.addEventListener('click', function() {
      // …
    });
  };

  document.addEventListener('DOMContentLoaded', function() {
    var $galleries = document.querySelectorAll('.gallery');

    for (var i = $galleries.length - 1; i >= 0; i--) {
      initializeGallery($galleries[i]);
    }
  });

}());
Enter fullscreen mode Exit fullscreen mode
Collapse
peerreynders profile image
peerreynders • Edited

Another practice that may be worth considering is using JavaScript Hooks (though it isn't universally accepted; see also Decoupling Your HTML, CSS, and JavaScript).

<section class="gallery js-gallery">
  <!-- other block elements -->
  <div>
    <button class="gallery__previous-button" name="previous-button">Previous</button>
    <button class="gallery__next-button" name="next-button">Next</button>
  </div>
  <div class="gallery__items">
    <!-- more -->
  </div>
</section>
Enter fullscreen mode Exit fullscreen mode

so

document.addEventListener('DOMContentLoaded', function() {
  var $galleries = document.querySelectorAll('.js-gallery');
  for (var i = $galleries.length - 1; i >= 0; i--) {
    initializeGallery($galleries[i]);
  }
});
Enter fullscreen mode Exit fullscreen mode

Now superficially this may come across as redundant but as stated by Harry Roberts:

I have known occasions before when trying to refactor some CSS has unwittingly removed JS functionality because the two were tied to each other—it was impossible to have one without the other.

It could also be seen as helpful that the js-* classes within the HTML clearly identify those blocks (or elements) that have behavioural enhancements (i.e. are "JavaScript components").

It isn't always necessary to use class-based JavaScript hooks on elements inside a block. Given that a block/component instance is already scoped on a particular fragment of the DOM it is possible to more easily select on non-class features or the fragment's internal structure to bind element behaviour.

var $galleryNextButton = $gallery.querySelector('button[name="next-button"]'),
    $galleryPreviousButton = $gallery.querySelector('button[name="previous-button"]'),
    $galleryItems = $gallery.lastElementChild;
Enter fullscreen mode Exit fullscreen mode

See also:

Thread Thread
malteriechmann profile image
Malte Riechmann Author

Thanks for the insights. I once tried the js- prefix, but did not like it that much. I like to use same classes for JavaScript and CSS.

And also thanks for showing me how to use syntax highlighting at DEV. I will update my post as soon as possible.

Collapse
zzoukk profile image
zZouKk

Thanks for this example (extended) and kudos to this loop counting backward 😆

for (var i = $cards.length - 1; i >= 0; i--){...}

Collapse
malteriechmann profile image
Malte Riechmann Author

Regarding the $ sign. Yes, I use it to point out which variables hold DOM elements.

Collapse
vladi160 profile image
vladi160

I am not sure, where is that BEM in JS, too, but in CSS, BEM is overpriced, like you fix a problem that doesn't exists just to think something is much more organized and at the end, the class names are longer that the content inside the element.

Collapse
malteriechmann profile image
Malte Riechmann Author

Thanks for sharing your opinion.

I have made other experiences. With BEM the frontend (HTML, CSS and JavaScript) gets more organized and it works really well for some of our teams.

Not everything works well for everyone. Some of our teams use TailwindCSS, which is a completely different approach.

What works for your team?

Collapse
honatas profile image
Jonatas de Moraes Junior

Just be careful: going frameworkless is a one-way trip. =)

Here is my small attempt: github.com/Honatas/frameworkless-j...

Collapse
peerreynders profile image
peerreynders

"Frameworkless" doesn't necessarily imply going "full vanilla". It's possible to use libraries like µhtml (and related) to take care of some of the tedious details while having your own code supply the plumbing of the client side page/application.

Everyone needs a framework; what everyone doesn't need is a general purpose framework. Nobody has a general problem, everyone has a very specific problem they're trying to solve.

Rasmus Lerdorf - original creator of PHP

Nonetheless having knowledge of the more common Web APIs on a detailed level (first) is invaluable.

Collapse
malteriechmann profile image
Malte Riechmann Author

Thanks for commenting.

I like your approach, but isn't it a bit of a framework? ;)

Collapse
honatas profile image
Jonatas de Moraes Junior

A very small one, nonetheless. Anything you do that tries to avoid code duplicity will end up looking like a framework anyway. The point of frameworkless is not to avoid frameworks at all, but to make things simple and keep the code under your control, in order to be able to eliminate as much technical debt as possible. Which I believe is exactly what you want to do with your approach: keep things simple, stick to the basics.
But yeah, I guess I could strip some stuff from there and go even simpler, but then I wouldn't have a Router anymore. Sad. ;)

Collapse
chrisczopp profile image
chris-czopp

It's my personal opinion but I think these days, unless you're web app doesn't require any JS, manipulating DOM manually is a very risky approach. It's very easy to get into a spaghetti call stack if you e.g. need to fetch and interpolate some data into DOM (especially when constructing lists). Having said that, I see why shifting towards roots using pure JS + HTML + CSS might look attractive. And I see how CV-driven development became so popular, and that over-engineering and using fancy tech-stack takes precedence over the product/feature being implemented. Luckily there are still minimalistic rendring libraries, micro frameworks, starter boilerplates or even entire self-contained tools which purpose is to make you productive.

Collapse
malteriechmann profile image
Malte Riechmann Author

Thanks for your opinion on this. I think it is safe to do simple DOM manipulating without a framework.

Collapse
christiankozalla profile image
Christian Kozalla

Thanks for this nice article! Vanilla HTML, CSS and JS are truly underrated.

Btw I like seeing fellow devs from Germany here on DEV

<= located near Chemnitz 😀

Collapse
malteriechmann profile image
Malte Riechmann Author

Thank you :)

<= located in Flensburg

Collapse
ronca85 profile image
ronca85

user rem only when you need to change font-size on the element that's being styled from the default 16px. for example, to use 20px use 1.25rem.
use em for border, border-radius, box-shadow and padding.
this will keep the proportions of the element the same no matter what the browser's zoom setting is set at. if someone uses 200% zoom level the element will scale correctly, as one unit.