CSS Animations

This post is supposed to be a beginners introduction to CSS 3 animations. There are two ways for animation in CSS 3, animations and transitions.

Differences between Animations and Transitions

This article sums it up nicely: https://www.peachpit.com/articles/article.aspx?p=2300569&seqNum=2

In general transitions are defined by a start and an end state. Animations can have an arbitrary amount or keyframes in between a start and a end state. Transitions are therefore suited for simpler use-cases, whereas animations are used when the requirement is complex.

Animations

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations

The main use-case for animations is

For animations, a trigger is optional. A animation can start without a trigger for example right after the page loads.

Animations can be created via .css files or via the element.animate() javascript WebAPI (https://developer.mozilla.org/en-US/docs/Web/API/Element/animate).

Transitions

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions

The main use-case for transitions is highlighting items such as navigation elements on hover. Instead of instantly switching the background color of such a navigation element to a darker shade, the transition can smoothly transition the color to the darker shade which gives the page a more relaxed and more organic feel.

A transition needs a trigger to run. This trigger can be the change of a CSS property or some JavaScript.

Transitions are added to CSS classes. A transition lists the properties of the same CSS class, that should be changed in a smooth animation. A animation always need a new value for a property so that a property can be animated as it transitions from the current value to the new value. The new value is not defined by the transition, in other words is not predefined. When a property, that is defined in the list of transitions, changes either by adding or removing a css class or using javascript, then the transition is triggered and will be applied so that the value is smoothly interpolated between the current and the new value.

Transitions are controlled using the transition-properties inside a CSS class. The transition properties are: transition-propertytransition-durationtransition-timing-function and transition-delay.

A shorthand notation is available that combines all properties above into a single line: transition: <property> <duration> <timing-function> <delay>;

Style a Image Slider

Introduction

By image slider, a component that displays a single image out of a set of images is meant. The slider contains controls to switch to a different image. The slider can be controlled via direct user input or indirectly via a timer that automatically operates the image slider.

The Purpose of a slider is similar to an automated marketing presentation or sales video. It is there to catch a visitor, generate attention and interest and to advertise an idea or product to a user that generally has no time to loose.

Features

Features of a slider are:

  • Display an array of images
  • Width, height, general responsiveness
  • Dotted button navigation, minimum, maximum amount of dots
  • Left-right arrow button navigation
  • Mouse- or Thumb Drag navigation (swipe left and right) 
  • Navigation wrap-around
  • Cooldown/Countdown timer that initiates the next transition automatically
  • User interaction with the dotted button navigation will interrupt the automated timer to give the user time. The timer will take over again after some time.
  • Transition default direction or pattern (To-Right, To-Left, Random, …)
  • Animation options for the transition between images (slide, shrink, grow, opacity, alpha, …)

Minimum Viable Product – MVP

Creating an MVP that contains the subset of all features that constitutes the bare minimum to be recognized by a user as a usable component that has benefit, is a strategy to arrive at a result without getting side-tracked and loosing focus.

Why is an MVP important? In short, the main problem is our limited experience when approaching a topic for the first time. Because of all the unknown road blocks a beginner will face on his learning journey, there is natural delay and natural feature creep. Features creep in because unknown requirements pop up and features have to be added to even get the most limited MVP working.

The image slider MVP will 

  • Display three images
  • Contain no navigation
  • Transition between images on click on the current image
  • Transition direction is fixed: to-right is used
  • Contains no wrap-around
  • The transition is not animated, the images just are exchanged
  • The slider is not responsive

MVP Implementation

The HTML markup contains the three slides

<div class="wrapper">
<div class="slides">

<div class="slide active">
First
<img src="../img/mountain-1.jpeg">
</div>

<div class="slide">
Second
<img src="../img/mountain-2.jpeg">
</div>

<div class="slide">
Third
<img src="../img/mountain-3.jpeg">
</div>

</div>
</div>

The CSS contains general styling for the slides which sets all slides into th e display:none state which hides all slides. in addition it contains a CSS class called active. This active class sets display:block on one of the slides to show that slide.

html, body {
width: 100%;
height: 100%;
}

.wrapper {
height: 100%;
}

.slides {
display: flex;
align-items: center;
justify-content: center;
}

.slide {
display: none;

width: 100px;
height: 100px;
background-color: red;
}

.slide.active {
display: block;
}

The JavaScript script registers a click listener on the slides container. In the click listener, the active slide is retrieved and based on the active slide, the indexes for the next slide are computed.

When the next index is computed, the active class is toggled both on the current and on the next slide, which will exchange the images.

function transition() {

// select the slides container element
let slidesElement = document.querySelector('.slides');

// select the NodeList of all slides
let slideElementsArray = slidesElement.querySelectorAll('.slide');

slidesElement.addEventListener('click', () => {
// select the active slide element
let activeSlideElement = slidesElement.querySelector('.active');

// use the prototype as a NodeList has no indexOf() method
let currentSlideElementIndex = Array.prototype.indexOf.call(slideElementsArray, activeSlideElement);

// find the next index
let nextSlideElementIndex = currentSlideElementIndex == slideElementsArray.length-1 ? currentSlideElementIndex : currentSlideElementIndex + 1;

// retrieve the next div element
let nextSlideElement = slideElementsArray[nextSlideElementIndex];

// toggle the active classes to display the next image
activeSlideElement.classList.toggle('active');
nextSlideElement.classList.toggle('active');
})
}

transition();

Style a FlipCard

Links

https://www.w3schools.com/howto/howto_css_flip_card.asp

Introduction

This post contains may notes on the example of a flip card from W3Schools here. The article is not written in a beginner friendly way, I personally feel. It could do with more text describing what each part of the markup and CSS does which this article tries to add.

FlipCards

A FlipCard is a rectangular area that has a front- and a back side. The 3D capabilities of CSS are used to turn the card around by 180 degrees to reveal the card’s flipside. This adds an interesting and interactive effect to a page and also saves some space for detailed information on the item displayed on the front of the card. 

Strategy

The HTML markup contains an outer div that is used to position the flip-card on the page.

Inside the outer div, there is an inner div which will rotate on hover. That inner div contains two nested divs. One is called front-side, the other one is called back-side. Both the front- and back-side are set to not render their back-facing side, that means, when the back-facing side faces the viewer, that side is not rendered by the browser.

Initially the nested front-side is not rotated or rotated by 0 degrees, whereas the back-side is initially turned around, so it is initially rotated by 180 degrees. The front- and back-side are not rotated any more from here on out, only the inner div is rotated.

HTML Markup

<div class="flip-card">
<div class="flip-card-inner">
<div class="flip-card-front">
<img src="https://picsum.photos/300/200" alt="Avatar" style="width:300px;height:100%">
</div>
<div class="flip-card-back">
<h1>John Doe</h1>
<p>Architect & Engineer</p>
<p>We love that guy</p>
</div>
</div>
</div>

You can see the outer flip-card to position the entire card and the inner flip-card that contains the front and back side divs.

CSS Styling

The outer flip-card contains the dimensions and the perspective attribute which introduces a real 3D rotation effect.

.flip-card {
background-color: transparent;
width: 300px;
height: 300px;
border: 0px solid #f1f1f1;
perspective: 1000px;
}

The inner flip-card has two styles, on normal style and one style on hover.

/* This container is needed to position the front and back side */
.flip-card-inner {
position: relative;
width: 100%;
height: 100%;
text-align: center;
transition: transform 0.8s;
transform-style: preserve-3d;
}

/* Do an horizontal flip when you move the mouse over the flip box container */
.flip-card:hover .flip-card-inner {
/* On hover, rotate the inner card which will rotate the front and backside with it */
transform: rotateY(180deg);
}

The nested front- and back-side share some styles but also have styles specifically for themselves. One important specific style is the initial rotate of either 0 degrees or 180 degress.

/* Position the front and back side */
.flip-card-front, .flip-card-back {
position: absolute;
width: 100%;
height: 100%;
-webkit-backface-visibility: hidden; /* Safari */
backface-visibility: hidden;
}

/* Style the front side (fallback if image is missing) */
.flip-card-front {
background-color: #bbb;
color: black;
/* initially not rotated */
transform: rotateY(0deg);
}

/* Style the back side */
.flip-card-back {
background-color: dodgerblue;
color: white;
/* initially rotated by 180 degree */
transform: rotateY(180deg);
}

Style a Checkbox as Toggle

Links

https://codepen.io/himalayasingh/pen/EdVzNL

What is this article about?

I found this wonderful codepen created by Himalaya Singh. In this pen, Himalaya takes a HTML checkbox and changes it into an ios style toggle button using pure CSS without any JavaScript. This is quite the useful codepen and this article contains my notes on how to best read and understand Himalaya’s CSS.

The CSS and HTML snippets in this article are not Himalaya’s original code (but still heavily inspired by it). I slightly modified the snippets during my analysis for the worse. So definitly check Himalaya’s original codepen after reading this article.

How it works

The overall strategy is to use a HTML input of type checkbox and then to hide it using an opacity of 0. That way the user cannot see the input but they can still interact with it.

In a second step, a so-called knob and a background layer are added to the toggle. The knob is moving from left to right and displays the checked state of the input. It also can contain a text. The layer acts as a visual border for the input. Both the knob and the layer contain a color.

The HTML input type checkbox has two states, checked and unchecked. CSS classes are used via a selector that selects both possible states. Within the CSS classes for each state, the knob and the layer are styled. A CSS transition is used to define how the styling changes when the input transitions between both of it’s states.

HTML and Styling 

A HTML input with type checkbox is created.

<div class="button r" id="button-1">
<input type="checkbox" class="checkbox">
<!--<div class="knobs"></div>-->
<!--<div class="layer"></div>-->
</div>

We’ll take care of the knob and the layer later.

The surrounding button is positioned.

.button
{
position: relative;
top: 50%;
width: 74px;
height: 36px;
margin: -20px auto 0 auto;
overflow: hidden;
}

Then the input element is styled. To hide the input, an opacity of 0 is used.

/* opacity 0 is entirely transparent, this hides the checkbox but lets the user interact with it still */
.checkbox
{
position: relative;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
opacity: 0;
cursor: pointer;
z-index: 3;
}

At this point you will absolutely see nothing any more on your page. To add graphical representation back, let’s start by adding the knob.

It is important to start with the knob because the knob is what gives the layer content. Starting with the empty layer causes the layer to collapse completely. A collapsed div is basically invisible, hard to style and generally a source of confusion.

Adding the knob

In the HTML, activate the knobs div by removing the comment around it. Also add a CSS class that positions the knobs div within it’s positioned parent.

/* styles the div that is inserted below the input checkbox html element */
.knobs
{
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 2;
}

Style the two states checked and unchecked of the knobs div.

#button-1 .knobs:before
{
content: 'NO';
position: absolute;
top: 4px;
left: 4px;
width: 20px;
height: 10px;
color: #fff;
font-size: 10px;
font-weight: bold;
text-align: center;
line-height: 1;
padding: 9px 4px;
/*background-color: #03A9F4;*/
background-color: #f44336;
border-radius: 50%;
transition: 0.3s cubic-bezier(0.18, 0.89, 0.35, 1.15) all;
}

/* Style for when the checkbox is checked */
#button-1 .checkbox:checked + .knobs:before
{
content: 'YES';
left: 42px;
background-color: #03A9F4;
/*background-color: #f44336;*/
}

The CSS selector above contains .checkbox:checked which is how the checkbox state is targeted using CSS. The first of the two states does not contain any state so this is the default unchecked state. A transition is contained in the first CSS class. The transition defines how the transition between both CSS classes is animated. This animation moves the knob from left to right and vice versa, it changes the text and the color.

Adding the Layer

To also style the layer, first uncomment the layer div in the HTML. Then, using the same principle as for the knobs, define two CSS classes one per checkbox state that define the appearance of the layer in each state and how the transition between the two states is animated.

/* The layer is the background that the slider knob is displayed inside.
It is inserted as a separate div below the input checkbox html element
The layer provides the visual appearance and the outline border around the ckeckbox. */
.layer
{
--dummy-style: foo; /**/
position: absolute;
border-radius: 100px;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
background-color: #fcebeb;
/*background-color: #ebf7fc;*/
/*background-color: #f44336;*/
transition: 0.3s ease all;
z-index: 1;
}

/**
The general sibling combinator is made of the "tilde" (U+007E, ~)
character that separates two sequences of simple selectors.
The elements represented by the two sequences share the same parent in the document tree
and the element represented by the first sequence precedes (not necessarily immediately)
the element represented by the second one.
*/
#button-1 .checkbox:checked ~ .layer
{
/*background-color: #fcebeb;*/
background-color: #ebf7fc;
/*background-color: #03A9F4;*/
}

#button-1 .knobs, #button-1 .knobs:before, #button-1 .layer
{
transition: 0.3s ease all;
}

Summary and Next Steps

The toggle works and looks awesome. Things that come to mind are, how to you get translated text onto the toggle? Maybe it is easier to not have any text on the knob of the toggle to save a lot of work. Also the CSS probably should be translated to SCSS if that is what your project uses. Another important part is to use the toggle in a form element of your framework of choice. A test has to be made if the input works nicely with Angular, Vue and React.

Organizing JavaScript

Links

https://en.bem.info/methodology/
https://www.webcomponents.org/specs

Prolog

This post is about JavaScript usage on a larger scale. Writing small snippets is one thing and you can get away with almost everything. Writing larger applications becomes a question of scaling. You need architectural patterns to linearily achieve progress when working on large applications.

Scope and Modularity

JavaScript has Scopes. The global scope is the parent scope that always exists. Without any further preparation, variables will be assigned to the global scope. That means all variables from code in script-tags or imported .js files will by default live in the global scope unless local scope is introduced to house variables.

The issue with global scope is that variable names can clash and code ultimately stops working. It prevents good organization and code reuse. Modularity and therefore local scope is needed.

Hoisting with the var Keyword

Hoisting means that variable declarations that make use of the var keyword are moved to the top of the current script (tag or file) or if they are defined within a function, they are used to the top of the current function. Only variable declarations are hoisted, initializations are not moved to the top. That means a hoisted variable can be undefined because the initialization is not moved to the top.

The effect is the same as seen in older versions of C. Variables can only be declared at the top of a function. Hoisting is the automatic process of moving variable declarations to the top of the function or script tag or script file.

Hoisting with the let Keyword

Variables defined with the let keyword are not hoisted all the way to the top of the function or script (tag or file) as is the case with the var keyword but they are hoisted within the nearest scope exclusively. Again, their initialization is not hoisted, so they can be undefined.

The nearest scope is defined by everything that is enclosed in curly braces. That means if-statements, function bodies, loop bodies and even an artificial block defined by an opening and a closing curly brace define local scope.

How to use var, let and const

Code is easy to read if you use const everywhere. If a variable value has to change, use let instead of const. Never use var.

Functions define local Scope

Each function introduces new local scope defined by the function body.

In the very good book Mastering modular JavaScript by Nicolás Bevacqua, the author lists three ways functions (to be precise, the pattern is called IIFE (Immediately-Invoked Function Expression)) have been used a few years ago to define local scope similar to modules:

(function() {
console.log('IIFE using parenthesis')
})()

~function() {
console.log('IIFE using a bitwise operator')
}()

void function() {
console.log('IIFE using the void operator')
}()

Blocks define local Scope

Not only functions define local Scope but blocks do. A function is a special case of a block.

In ES6 this code is possible

{  // open block    
  let tmp = ···;
···
} // close block

The let keyword creates a variable in the local scope (as opposed to the var keyword which  creates a variable that is hoisted to the top of the function which potentially changes the scope it is defined in.

Modules define local Scope

ECMAScript 2015 (ES6) introduced modules (ECMAScript Modules (ESM)) as part of the JavaScript language. In node (which uses CommonJS by default, the ESM system is available only when specifying –experimental-modules and using the .mjs extension for modules)

Before ES6, custom libraries (CommonJS, RequireJS) provided module functionality for JavaScript programmers. Those custom libraries are still used extensively today. Probably because it is a massive undertaking to refactor all existing code to ES6.

So now there is a mix of CommonJS, RequireJS and ES6 modules used in the wild. The syntax for ES6 modules (export, import) differs from the CommonJS and RequireJS syntax (use of the exports-object, require).

While CommonJS is the dominant module system in node, RequireJS is more geared towards browsers. RequireJS implements the Asynchronous Module Definition (AMD) standard. Typescript adopted the ES6 module syntax from the start.

Later Browserify allowed to bundle node bundles into a browser-ready format and allowed the use the node package manager and all it’s modules in the development of web applications that run in a browser. Today webpack is the most widely used bundler and mostly took over Browserify.

Across all possible module systems, the common parts are that every file is a module and a module cannot be spread across multiple files. Every module has it’s own scope and context.

Object Orientated Programming (OOP)

To me, object orientation is about combining state and functions that allow you to access and manipulate that state in one place that you can easily find even in large applications.

Even after weeks of not working on a project, it is clear that a persons name and address are stored in the Person class. It just makes sense and is intuitive.

In Java and C++ for example, first a Class has to be defined. The class definition controls which member variables and which function a class has. At runtime variables (aka. objects or instances) are created from the class. No member variables or functions can be added or removed.

JavaScript is different in many ways. An object can be created without a class definition. At runtime, member variables and functions can be added and removed from objects.

OOP using Object Initializers

let personInstance = {
name: 'person1',
age: 30
}

// location A

personInstance.isBlocked = true;

// location B

delete personInstance.isBlocked;

// location C

In the example above, an object is defined (using JavaScript Object Notation (JSON)) without a class and without a constructor function (see below)! Instead a so called object initializer is used, see https://developer.mozilla.org/de/docs/Web/JavaScript/Guide/Working_with_Objects

The object initializer is a block of JSON that defines the object, it’s member variables and it’s functions. For alternatives to object inititalizers look at constructor functions and classes!

In the rest of the script, a member variable ‘isBlocked’ is added and initialized and then removed again. console.logging personInstance.isBlocked at location A yields ‘undefined’ as the isBlocked member is not yet part of the object. At location B, logging will yield true as expected. At location C, logging will again yield ‘undefined’ as the member was removed.

let app = {

settings: {
container: document.querySelector('.calendar'),
calendar: document.querySelector('.front'),
days: document.querySelectorAll('.weeks span'),
form: document.querySelector('.back'),
input: document.querySelector('.back input'),
buttons: document.querySelector('.back button')
},

init: function() {
console.log('container: ', this.settings.container);
console.log('calendar: ', this.settings.calendar);
console.log('days: ', this.settings.days);
console.log('form: ', this.settings.form);
console.log('input: ', this.settings.input);
console.log('buttons: ', this.settings.buttons);
},
}

app.init();

The code above combines data (settings) and functions (init()) into an object (app). Then it calls a method on the app instance. The call will output the state stored in that instance.

There is a shorthand notation to add a function to an object. Instead of using the key value pair notatino ( functionName: function { … } ) you can use functionName() { … }. In the example above init() { … } instead of init: function { … }

The keyword: this

In JavaScript, this used inside a function, refers to the object that called the function. If the function is a member function of an object, that behaviour is not changed. In JavaScript, the this keyword has no relation to the objects instead it has a relation to the caller.

In Java and C++, this used inside a member function refers to the object instance. Here, the this keyword has no relation to the caller!

In DOM event handlers such as click handlers, this refers to the object that emitted the event.

When a function is defined in global scope and the script is executed inside a browser, this refers to the window object. When strict mode is enabled in addition, the this keyword in global scope is undefined and does not point to the window object!

With arrow functions, the behaviour of the this keyword is different than for normal functions. So arrow functions are not syntactic sugar but they have their own characteristics. Arrow functions do not have an own this pointer! Because they have no own this pointer, when calling an arrow function, they do not bind the this pointer to the context of the call! That in turn means that arrow functions do not shadow a this pointer that might exist before calling the arrow function. Because you can still use the this keyword inside an arrow function, the question remains, what the this pointer refers to inside an arrow function! This good news for OOP is that this inside an arrow function defined inside an object has a relation to the object that the arrow function is defined inside! this in an arrow function allows you to access the member variables and functions of the object instance!

When the this keyword is used inside a constructor function, this points to the newly created object instance.

Constructor function is

https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Functions/Arrow_functions

CONSTRUCTOR FUNCTIONS

Instead of defining objects using the JSON notation, there is another way called constructor functions.

function Person(first, last, age, eye) {
  this.firstName = first;
  this.lastName = last;
  this.age = age;
  this.eyeColor = eye;
this.name = function() {
    return this.firstName + " " + this.lastName;
  };
}

const myFather = new Person("John", "Doe", 50, "blue");
const myMother = new Person("Sally", "Rally", 48, "green");

console.log(myFather.name());

The example is taken from here: https://www.w3schools.com/js/js_object_constructors.asp

A function called Person is defined and later used in conjunction with the new keyword to arrive at instance variables myFather, myMother.

The Person() function is referred to as the constructor function. Inside the constructor function, the this keyword actually points to the instance that is just being created. 

OOP with Prototypes

Funtions can be added to the prototype and can then be called later.

OOP With Classes

The class keyword in javascript is syntactic sugar for JavaScript’s prototype system. That means the compiler or interpreter transforms the keywords into other JS features so the programmer is freed from the task.

Classes where introduced in ES6 (ECMAScript 2015)

Epilog

Especially with languages, I am no proficient in, I personally find myself in a situation where my progress keeps getting slower and slower over a day of developing software, just because I keep battling the language and how to organize the code when the application gets bigger. It gets slower until I get to a complete stop and I have to give up for the day. The next day there is even less progress. It becomes similar to wading through a swamp and you are finally so tired that you give up and the swamp swallows you. 

With languages I am proficient in, I find that I am not blocked by the language itself. I am blocked by medium to hard problems I have to solve but the programming language is at tool that makes it easier to solve the problems rather than slowing me down.

When you find yourself in a situation where the language is slowing you down, you have to realize that your programming skills in that language are lacking and you have to go back to school.

This article showed ways to use JavaScript which are applicable to larger problems

webpack

webpack is a build system for JavaScript which requires node to run. It refers to itself as a static module bundler. It views your source files as modules and organizes modules and their dependencies inside a graph. It will output one or more bundles after traversing the graph. So modules in a dependency graph in, bundles out.

webpack uses one entry point which is similar to a main() function in a programming language as it marks the starting point of operation. The entry point, entry for short, is the module where webpack starts to traverse the dependency graph.

Loaders do Load Modules

In webpack, you import modules to build up the dependency graph. A module can be any file, as long as there is a loader for that type of file. By default, webpack understands JavaScript and JSON files and it can convert those into modules and add them to the dependency graphs via import statements.

Additional loaders allow webpack to understand other types of files, convert them into modules and add them into the dependency graph.

When webpack sees an import, it looks into it’s definitions of loaders and if it finds a matching loader, it applies that loader to the import. The module rules for loader definitions are contained in the webpack.config.js.

const path = require('path');

module.exports = {
  output: {
    filename: 'my-first-webpack.bundle.js',
  },
  module: {
    rules: [{ test: /\.txt$/, use: 'raw-loader' }],
  },
};

In the rule above, test defines which import regular expression the loader will match and the use-part defines the loader implementation to use when the regex matches. In this example, the raw-loader will be applied to all imported .txt files.

Ultimately, your resulting bundle or bundles will contain all the JavaScript, HTML, CSS, images and other files that you import as modules. That’s right, you treat CSS files and everything you need as a module when using webpack. You will in fact import CSS files! Pretty exciting concept if you ask me!

The Entry

webpack uses a configuration file webpack.config.js. Here you specify the entry:

module.exports = {  
entry: './path/to/my/entry/file.js',
};

The entry will be a file called ./src/index.js in most cases.

Plugins

Plugins are added via the webpack.config.js file. They are then called by the webpack compiler during compilation.

A example configuration is

const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const webpack = require('webpack'); //to access built-in plugins
const path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    filename: 'my-first-webpack.bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: 'babel-loader',
      },
    ],
  },
  plugins: [
    new webpack.ProgressPlugin(),
    new HtmlWebpackPlugin({ template: './src/index.html' }),
  ],
};

here, the HTMLWebpackPlugin is used.

Example

First, create a node project.

cd dev/javascript
mkdir webpack_helloworld
cd webpack_helloworld
npm init -y
code .

Then install the dependencies.

npm install --save-dev webpack webpack-cli
npm install --save-dev html-webpack-plugin

Setup the files and folders.

insert a webpack.config.js next to the package.json.

const HtmlWebpackPlugin = require('html-webpack-plugin');

const path = require('path');module.exports = {
mode: 'development',
entry: './index.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index_bundle.js',
},
plugins: [new HtmlWebpackPlugin()],
};

This webpack.config.js requires a index.js file, so you have to create one next to the webpack.config.js. Inside the index.js file, just output some text.

console.log('webpack works! - Hello World!');
alert('webpack works! - Hello World!');

To start the webpack build, add a script in the package.json

"scripts": {    
"build": "webpack --config webpack.config.js",
"test": "echo \"Error: no test specified\" && exit 1"
}

Start the webpack compilation

npm run build

Now check your project folder. There is a dist folder generated for you. Inside that dist folder, the HtmlWebpackPlugin has generated a index.html file that imports a generated index_bundle.js. The index_bundle.js file contains all entry points and their dependencies defined in webpack.config.js. That means it will contain the code from index.js in this example.

Revisiting the Result

Now, webpack created a dist/index.html file for us and it bundle all entry modules (currently javascript files) and all their dependencies and imported them automatically into the dist/index.html file.

This is a wonderful situation for building a framework that generates all HTML markup programmatically. You could build all HTML via JavaScript’s DOM API from your javascript entry point. This is not necessarily what you want. If you want to use webpack and it’s hot reload feature to work with CSS and HTML, then you most likely want a HTML file you have full control over.

Using HTML with webpack

The question is, how do you add your own html markup into the generated index.html? The answer to this question is the HtmlWebpackPlugin’s template feature. Credits go to this solution: https://stackoverflow.com/questions/39798095/multiple-html-files-using-webpack/63385300 and the documentation https://github.com/jantimon/html-webpack-plugin/blob/main/docs/template-option.md

A template is a HTML file that you put into a src folder and that you configure in the HtmlWebpackPlugin in webpack.config.js:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
mode: 'development',
entry: './index.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index_bundle.js',
},
plugins: [new HtmlWebpackPlugin({
filename: 'index.html',
template: 'src/index.html',
chunks: ['main']
})],
};

A valid template (src/index.html) looks like this:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Webpack App 2</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<h1>Hewlo Wurl!</h1>
</body>
</html>

Webpack will first copy this file into the dist folder and then webpack will modify the copied file. Webpack will insert a script-tag into the header and that way it will import the compiled modules (bundles) into the html file copied from the template.

To try this out, build again (npm run build) and reopen the dist/index.html in your browser. You should first get an alert box which is contained in the entry module index.js (if you followed this example) and then you should see the custom HTML from the index template! Wonderfull, we are almost there!

The next question is, how to import CSS.

Using CSS with webpack

Let’s say you want to use ./src/index.css

h1 {
color: red;
}

First install the webpack css-loader and the style-loader

npm install --save-dev css-loader
npm install --save-dev style-loader

Next add a rule into webpack.config.js that makes webpack apply the css plugin to css files:

const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");

module.exports = {
mode: "development",
entry: "./index.js",
output: {
path: path.resolve(__dirname, "./dist"),
filename: "index_bundle.js",
},
plugins: [
new HtmlWebpackPlugin({
filename: "index.html",
template: "src/index.html",
chunks: ["main"],
}),
],
module: {
rules: [
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
],
},
};

No this is where it gets a little weird, at least to my liking. You will use an import statement to import your css file into the entry. The import will make webpack apply the css-loader and style-loader to the css file.

import css from "./src/index.css";

console.log('webpack works!');
alert('test');

Now rebuild and reload the generated index.html. The header should be displayed in red color.

Using JSON files with WebPack

Say you have a JSON file on your hard drive that contains JSON data you need process in some JavaScript routines. You can import the JSON file using a Promise and process the data once the Promise is successfull.

import(
'./data/testdata.json'
).then(({default: testdata}) => {

let jsonOutputElement = document.getElementById("rawJson");
jsonOutputElement.innerHTML = JSON.stringify(testdata, undefined, 2);

// do whatever you like with your "jsonMenu" variable
//console.log('testdata: ', testdata);
initTreeStructure(testdata);
});

For webpack to load the JSON file, it uses a JSON-Loader, that not only reads the file to a string but it parses the JSON to a JavaScript object.

In earlier versions of webpack, a json loader had to be installed manually and added to the list of loaders in webpack.config.js

npm install json-loader --save-dev
module.exports = {
module: {
loaders: [
{
test: /.json$/,
loader: 'json-loader'
}
]
}
}

It seems that manually adding a JSON-loader in newer versions of webpack actually causes issues because the added JSON-loader conflicts with the onboard JSON-loader leading to parse errors during JSON parsing! I found that in the current version of webpack, it is sufficient to just import JSON files without installing and configuring any JSON-loader.

Using HTML components with WebPack

https://medium.com/hackernoon/using-html-components-with-webpack-f383797a5ca

Hot Module Replacement aka. Hot Reload

webpack can be instructed to watch your files for changes, compile and reload the page in the browser for you. That way the latest changes are available on save.

To enable hot reload, first install webpack-dev-server

npm install webpack-dev-server --save-dev

Now, edit package.json and add a serve script:

"scripts": {
"build": "webpack --config webpack.config.js",
"serve": "webpack serve",
"test": "echo \"Error: no test specified\" && exit 1"
},

Run the serve script

npm run serve

This will bring up a webpack development server with hot load capabilities. In the console, a URL is printed. You have to open that URL using a browser to get the hot loaded page. Do not just open the html page in your dist folder, this file will not be hotloaded. Your browser has to access the webpage from the hot loaded server!

Test it out, change your css or your JavaScript. The browser is instructed to reload the page and your changes are immediately reflected in the browser.