Refactoring the Project – Learn Typing – Part 1

Last year I created a small “learn-typing”-project to learn ReactJS. Looking back let’s see how the “legacy” code holds up and let’s clean up a couple of things to make the code more readable in the future.

package.json

The first thing to take a look at is the package.json. One thing that I did not take particular care of was how I installed packages. I simply used ‘npm i <package>’ and called it a day.

However you should differentiate:

  • ‘dependencies’, modules used during runtime
  • ‘devDependencies’, modules used during development.

To clean things up I moved all @types packages, react-scripts and typescript to the correct ‘devDependencies’.

Now that everything is organized we can look if further actions are needed.

Security Issues

It is important to keep the dependencies in production up to date. The major concern is to fix security vulnerabilities. We can run the command:

npm audit fix --only=prod

This will upgrade the packages and apply the available patches for the issues.

Updates

We can also update all packages to the newest package version. This however may come at the cost that code needs to be adjusted to work with the new version of the packages.

My approach is updating single packages and then see if everything still works as expected, and then update the next package.

To see which packages need to be updated I use the VSCode Plugin “Version Lens” another popular alternative is using the CLI-tool ‘npm-check

Licences

Now the last thing, (or actually the first thing) we need to check are the licenses used by our project and if we can use the modules in production.

With the NPM tool ‘license-checker‘ we can quickly analyse our packages.

npm i -g license-checker

The command:

license-checker --onlyunknown --production

Takes a look at all packages used during production that do not have a standard licence.

@Blueprintjs/core – uses a slightly modified Apache 2.0 license. It has an additional paragraph 10

To figure out what is going on you now must go to the specific package and read up on what the exact license is. There are now two approaches: you get a legal team to figure out how this modification will affect your product, or you try to replace this dependency with an alternative.

In the next step of the refactoring I will take a look at how I can replace this dependency with a custom package.

Posted in Web

Basic Setup of TypeScript with Jest

We will take a quick look at how to set up a project using TypeScript and Jest. By adding Jest to the project from the beginning this should encourage test driven development – or at least that developers have minimal effort

Init Project

npm init -y
npm i -D typescript ts-node
tsc --init

npm i -D jest ts-jest @types/jest
npx ts-jest config:init

mkdir src
mkdir dist

Configure Project

tsconfig.json

For TypeScript you need to adjust a couple of variables like:

  • outDir = “./dist”
  • rootDir = “./src”
  • (optional) target: “es2017” (if you are not supporting IE11)
  • (optional) module: “esnext”
{
  "compilerOptions": {
   
    "target": "es2017",                          
    "module": "esnext",
    // ...
    "outDir": "./dist",  
    "rootDir": "./src",
    // ...
  }
}    

package.json

the only thing that remains is to add a couple of scripts to make development easier.

  "main": "./dist/index.js",
  "scripts": {
    "start": "ts-node ./src/index.ts",
    "test": "jest --watch",
    "build": "tsc"
  },

Init Git

It is always the best practice to use version control for coding projects – even if you are the only developer. Running Jest in “–watch” mode requires that the code is stored in a git repository

.gitignore

You need to tell git to exclude the node_modules folder. (This is done as the dependencies can be restored by running npm install). Create a file called “.gitignore” and add following line:

/node_modules

First commit

Since you have excluded

git init
git add .
git commit -m "Initial Commit"

Start Development

Demo Files

Create the files:

  • ./src/index.ts
  • ./src/index.test.ts

index.ts

export const greeting = () => {
  return "Hello World!"
}

index.test.ts

import { greeting} from "./index";

it('greets you', () => {
  expect(greeting()).toBe("Hello World!");
})

Run Demo / Start Development

Now you can start development by running

npm test

This will start jest and run your tests as soon as you change your code.

What is good code?

This is one of the biggest questions in programming. Everybody has an subjective answer to the question, as everybody develops their own style of programming over time.

For me good code has to fulfill these 5 criteria:

  1. Correct
  2. Efficient
  3. Simple
  4. Readable
  5. Maintainable

Now lets take a closer look at every criteria and what it means for your programming project

Correct

Code should have one key feature: It should work. Not functioning code is useless code.

Code has a very specific task it should do and it only should do that task. It should not do anything in addition that you do not expect.

Correct code also implies that it was built to specification . If you have a specification, you also can ensure that your code is correct by writing tests that verify that the code is doing exactly that what was specified.

Simple

If you have the choice between a complex expression and a simple expression that solves the same problem. then use the simple expression.

The complex expression is always a big door for unintended bugs. Additionally it makes it much harder to explain what is going on with your code.

Efficient

Efficiency, strongly depends on the values / non-functional requirements of your project.

It means if you can avoid costly operations, duplicate operations or unneeded operations. then you should optimize your code to avoid these kind of structures.

If possible you also should optimize for performance, however for most algorithms you need to decide a trade-off if your code will use a lot of memory or a lot of CPU. Before optimizing you always have to define for what you are optimizing.

Readable

Code is written for humans. Another human should be able to read your code.

If you use complex structures it just makes it harder for someone else to figure out what is going on in your project.

The compiler does not care if the variable is called “f” or “isActive” in both cases the compiler produces the same bitcode. Naming things is very important.

And never forget in most cases you are going to be the one that will revisit the piece of code and you will not know anymore what “f” is, but will immediately understand what “isActive” means.

Maintainable

Code is never done. There are a lot of things that causes that code needs to be adjusted in the future:

  • Additional Requirements
  • New Features
  • Dependency Upgrades

Now you can say, screw the maintainer of the project, you are only concerned about today. – However in most cases the guy that needs to fix something is the guy that created the code in the first place. – So do yourself a favor and write maintainable code.

What does it mean to write maintainable code? Have documentation, encapsulate functionality in a way

Conclusion

In the end good code is whatever you want it to be. If you are working with embedded systems and you want to create the most efficient algorithm, you will probably have other values as when you are working on a Web-Project.

You can define whichever values you want. The important thing is that these values must be clearly communicated to your team. Ideally by you leading as an example the others can follow.

In the end the goal of good code is that your project should deliver on time, suffer from less bugs, new team members can be easily onboarded, the team writes better tests and overall better communication (the specifications have to be more precise) etc.

Sources:

ImageDesigned by Freepik

Getting started with MOBX 5 and TypeScript 3, React 16.6

When looking around for example applications that use Mobx 5.x combined with Mobx-react 5.xand TypeScript 3.x I did not find any useful example. Most of the examples in the awesome mobx list reference Mobx 3.x and are really outdated.

In my example I will use MOBX to display “Hello World!”. It should showcase a couple of core concepts you will be using in an mobx application.

tl:dr: Source Code for this example can be found here.

Inititalizing the project

First we need to create our project. I will use following scripts to intitialize the project.

Create-react-app with TypeScript

npx create-react-app mobx-example
cd mobx-example
npm install --save typescript @types/node @types/react @types/react-dom @types/jest

npx mv 'src/App.js' 'src/App.tsx' 
npx mv 'src/App.test.js' 'src/App.test.tsx' 
npx mv 'src/index.js' 'src/index.tsx'

npm start

Note: I am using the npm package ‘mv’ to rename the files, this ensures that the script will work cross-plattform.

We need to run start in order to let create-react-app initalize all typescript configuration files.

MOBX

npm install --save mobx mobx-react

In the tsconfig.json you need to ensure that experimental Decorators are enabled. Add following line to your tsconfig:

"experimentalDecorators": true

Getting Started with MOBX

In order to use a MOBX Store you need to create a couple of things:

  • A Mobx Store
  • Configure the provider
  • Use the store in a component

The Mobx Store

Store Directory

It is advisable to keep your stores organized in a single directory like ‘src/stores’.

mkdir src/stores

The Example Store

From the offical documentation to create a store it would be enough to create a class as follows:

mobxStore.ts:

import {observable, action, computed} from 'mobx';

class MobxStore {
    @observable name = "World";

    @computed 
    public get greeting():string {
        return `Hello ${this.name}`;
    }

    @action.bound
    public setName(name:string):void {
        this.name = name;
    }
}

export mobxStore = new MobxStore();

Note: Using @computed is just a demonstration how you could create a calculated value from the current state.

When using TypeScript this is not be enough, you need to create an interface and let our Store implement it to ensure type safety in the react component.

export interface IMobxStore {
    name: string;
    greeting: string;
    setName(name:string):void;
}

Additionally we will move the initialization to its own class.

Finally our mobxStore.ts looks like this:

import {observable, action, computed} from 'mobx';

export interface IMobxStore {
    name: string;
    greeting: string;
    setName(name:string):void;
}

export class MobxStore implements IMobxStore {
     @observable name = "World";

    @computed 
    public get greeting():string {
        return `Hello ${this.name}`;
    }

    @action.bound
    public setName(name:string):void {
        this.name = name;
    }
}

Note: The interface could also be moved into a type definition file.

Store initialization

We will now create a file src/stores/index.tsIn this file we will create an object ‘stores’ that will initialize all stores that we are using in our application.

index.ts:

import { MobxStore } from "./mobxStore";

export const stores = {
    mobxStore: new MobxStore()
}

Configuring the Provider

Since we are ensuring that all stores are inititalized in a single object the configuration of the Provider is very simple In the file src/index.tsx you need to import the store object and the Provider from mobx-react:

import {Provider} from 'mobx-react';
import { stores } from './stores';

Then you need to wrap the <Provider />around the <App />. By using the spread operator for stores you ensure that all stores are registered in the provider.

<Provider {...stores}>
    <App />
</Provider>

Using the Store

In order to use a store in a component you need to inject the store into the component and (most of the time) you will also want to observe the store for changes.

The store will be accessable via the properties. Thus we need to define an interface containing an optional variable ‘mobxStore’ of the type IMobxStore. It needs to be optional due to TypeScript not knowing that the Store is provided by the inject method. If it would be mandatory TypeScript would throw an missing props error when using <App />.

This in turn causes TypeScript to complain that const {greeting} = this.props.mobxStore; is not allowed, as this.props.mobxStore could be ‘undefined’. By adding the Non-null assertion operator ‘!’ you can signal the TypeScript compiler to ignore this warning.

App.tsx:

import React, { Component } from 'react';
import './App.css';
import { observer, inject } from 'mobx-react';
import { IMobxStore } from './stores/mobxStore';

interface AppProps {
  mobxStore?: IMobxStore
}

@inject('mobxStore')
@observer
class App extends Component<AppProps> {
  render() {
    const {greeting} = this.props.mobxStore!;
    
    return (
      <div className="App">
        <header className="App-header">
            {greeting}
          <button onClick={this.clickHandler}>Change Greeting</button>
        </header>
        
      </div>
    );
  }

  private clickHandler = () =>{
    const {setName} = this.props.mobxStore!;
    setName("Bob");
  }
}

export default App;

Conclusion

Now if you run the application you should now see a wonderful “Hello World!” greeting and by clicking on the button it changes to “Hello Bob!” using mobx and typescript.

I hope this makes it a little simpler to get started with React, Mobx and TypeScript.

If you are interested in the project files you can get them from GitHub https://github.com/borgfriend/typescript-react-mobx-example

Dev Environment to Learn TypeScript

TypeScript is basically JavaScript with types. When writing your code you must define the types of your variables and optionally the returns of the functions. If you have been working with Java or C++ these concepts will feel very familiar.

However there is a small problem, TypeScript is not JavaScript – thus you need to use a compiler to convert your code into JavaScript.

This guide will take a look at how to set up a simple developer environment in order to play around with TypeScript.

Tools needed:

  • NodeJS 11 (It does not matter if you are using the LTS Version or the current Version)
  • VSCode

New Project

In order to create your first project you have to create a directory and initialize it with npm. You can use the Command Line and enter following:

mkdir learn-typescript
cd learn-typescript
npm init -y
code . 

When VSCode is open you can open a Terminal using Ctrl-`

TypeScript

Initial Setup

npm install typescript

this will install the typescript compiler (tsc) to your project. You then could run commands like tsc index.ts and it will compile it to index.js. Which you could execute with node index.js.

However you cannot directly debug your code and it is annoying to run the compile before execution.

Add TS-Node

TS-Node is a tool that allows you to directly execute TypeScript code. It uses the existing tsconfig.jsonto process the code.

npm install ts-node

now you could run the code directly via a command like:

ts-node index.ts

Configure VSCode Debugging Tools

Ts-Node also allows you to use the VSCode debugging tools.

Press Ctrl-Shift-P and enter “Debug: Open launch.json” (this will create a launch.json) Select “Node.JS”

{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Launch current file w/ ts-node",
      "protocol": "inspector",
      "args": [
        "${relativeFile}"
      ],
      "cwd": "${workspaceRoot}",
      "runtimeArgs": [
        "-r",
        "ts-node/register"
      ],
      "internalConsoleOptions": "openOnSessionStart"
    }
  ]
}

Now when you press F5 you can run and debug your typescript code. This also allows you to set breakpoints in VSCode.

Your first “Hello World”

Now create a new file called index.ts and add following code:

async function greeter(name: string): Promise<void> {
  await console.log(`Hello ${name}`)
}

greeter("World");

This “Hello World” takes advantages of Modern ES2017 Features “async/await” as well as adding types for TypeScript.

When you press F5 in VSCode this should result in an error as Promises are not available in ES5. We can fix this by configuring the compiler.

Configuring the compiler

In order to configure the compiler you need to run the command:

tsc --init

this will create a tsconfig.json file. with this file you can configure the compiler. When creating it with the command it creates the file with the defaults and comments on what each compiler flag does.

One of the settings you should adjust is the “target” attribute. Currently it defaults to “es5” – this is because Internet Explorer 11 only supports ECMAScript 5.

We are targeting with our code NodeJS 10+, so we can change this value to “es2017”. As you can determine from this table

Running Hello World again

Now press F5 again the output should show you “Hello World”.

Conclusion

Now you are set to experiment some more with TypeScript.

One of the things you can experiment with is changing the target version of the compiler and running tsc index.ts and take a look at the resulting JavaScript code. You will see that compiling to es5 or es6 the compiler adds a lot of extra code to enable functioning code, however when compiling to es2017 it (almost) only strips away the types and leaves the code practically identical.

Attachments:

Compiling the code to ES5

To fix the code to compile to ES5 we first need core-js a library that provides polyfills for older versions of JavaScript.

npm install core-js @types/core-js

now we must add the line index.ts:

import 'core-js/es6/promise';

async function greeter(name: string): Promise<void> {
  await console.log(`Hello ${name}`)
}

greeter("World");

and in our tsconfig.json: we must add:

"lib": [
      "es2015",
      "dom"
    ],

Now your code should compile and run on IE11.

Visual Studio Code Extensions (September 2018)

Lets take a look at a couple of very useful Visual Studio Code Extensions.

General

Settings Sync

The tool allows you to synchronize your settings on multiple computers and/or operating systems. You need to have a github account and it will create a secret gist in order to sync your settings. Read More

Settings Sync

Code Runner

Basically it adds a small play button at the top of every file and lets you immediately execute it.

Code Runner

Git Lens

Git Lens dramatically enhances the GIT experience in VSCode. Inline display of code authorship, easy comparison with previous versions of the file and many more features.

Git Lens

Code Quality

TODO Highlighter

It highlights your comments that start with \\ TODO: or \\ FIXME:. Additionally it adds a command to list all Todos in your source code so you can identify and complete / fix the code.

If you use a different convention in your codebase you can configure the plugin to fit your use case.

TODO Highlighter

Spell Checker

Basically this complains when you start creating variable names that have no meaning. While ensuring that your documentation does not contain any spelling mistakes.

You can add words to a project dictionary And it is easily expandable to support additional languages.

Spell Checker

TSLint

It uses the local tslint.json and integrates the information into VSCode. You can enable ‘tslint.autoFixOnSave’ that automatically applies the defined rules (that can be autofixed).

TSLint

Theme

Cobalt2

A great dark theme with high contrast color choices. Personally I do not follow the official documentation on how to set up the theme. (Too many steps and it requires the font Operator Mono that costs 200USD )

Cobalt2 Theme Official

VS Code Great Icons

Well better Icons for VS Code

Great Icons

Image: Designed by Freepik

HTML: How to control the Form ‘autofill’-autocompetion

Image Designed by Freepik

The ‘autofill’ feature of browsers is a blessing and a curse. The feature enables the browser to reuse the data the user has previously entered in forms. The trouble starts when a form does not fulfill the standard. Then the feature sometimes fills in the wrong information into the fields.

Why does autofill fail?

The standard implemented a feature to group fields. This was created by adding optional ‘8characters at the start of the name’. A field like “first-name” could be interpreted as the group: “first” and autofill-field: ‘name’(=> last name). To correct this you would need to rename the field to ‘given-name‘ as this is defined in the standard. However, if you would test ‘first-name’ in the browser you will discover that autocomplete works without problems. That is because even though it is not in the standard certain aliases are detected correctly. Depending on how the browser interprets the form. If you are not using the standard you basically are telling the browser to guess what the field is and let the browser decide what to autofill.

The Standard Keywords

If you want a pleasant user experience for the visitors of your site then you should take the time to make sure that your form confirms to the standard keywords:

  • “name”
  • “honorific-prefix”
  • “given-name”
  • “additional-name”
  • “family-name”
  • “honorific-suffix”
  • “nickname”
  • “username”
  • “new-password”
  • “current-password”
  • “organization-title”
  • “organization”
  • “street-address”
  • “address-line1”
  • “address-line2”
  • “address-line3”
  • “address-level4”
  • “address-level3”
  • “address-level2”
  • “address-level1”
  • “country”
  • “country-name”
  • “postal-code”
  • “transaction-currency”
  • “transaction-amount”
  • “language”
  • “bday”
  • “bday-day”
  • “bday-month”
  • “bday-year”
  • “sex”
  • “url”
  • “photo”

For Credit Card Information:

  • “cc-name”
  • “cc-given-name”
  • “cc-additional-name”
  • “cc-family-name”
  • “cc-number”
  • “cc-exp”
  • “cc-exp-month”
  • “cc-exp-year”
  • “cc-csc”
  • “cc-type”

To use these keywords with attribute autocomplete. However if the field autocomplete is not set browsers will automatically check the attribute-name. In this example both fields would be detected by autofill as ‘name’:

    <form>
    <input name="field1" autocomplete="name" />
    <input name="name" />
    </form>

As a personal preference, I would not set the autocomplete-attribute and simply use the keywords in the field name. This immediately eliminates the need to think of a more suitable name. If you are dealing with a backend you cannot modify I would use the field autocomplete.

Groups

Sometimes you need some of these fields multiple times. Let’s say you need a shipping and a billing address.  Then you simply add a prefix to the attribute.

    <form>
      <input name="billing-street-address" />
      <input name="billing-country" />
      <input name="shipping-street-address" />
      <input name="shipping-country" />
    </form>

How to disable autofill?

In the rare case that you do not need this feature at all you probably would want to disable it. To do this you have again two options:

Disable globally

Simply add the attribute autocomplete="off to the form-element. You then can re-enable the autofill on an individual component by using autocomplete=on.

    <form autocomplete="off">
      <input 
        placeholder="disabled Autocomplete" 
      />
      <input 
        placeholder="enabled for only this input" 
        autocomplete="on" 
      />
    </form>

Disable Individual Input

You can disable the autofill feature for an individual component by adding autocomplete="new-password" (This is because Chrome ignores the ‘off’ value)

    <form>
      <input 
        placeholder="disabled only for this component"
        autocomplete="new-password" 
      />
    </form>

Sources:

How to publish on Github Pages with create-react-app and react-router

Now I will be assuming a couple of things:

  • You have a GitHub account and you created a repository for your app.
  • You created a react app either with npx create-react-app <appName> or npx create-react-app <appName> --scripts-version=react-scripts-ts
  • Then let’s get started.

Add gh-pages

Run npm i -D gh-pages\

Configure your package.json

First, you need to tell the react app where it will be hosted. You need to add to your package.json the attribute ‘homepage’:

    { ...
    "homepage": "https://<UserName>.github.io/<RepositoryName>/"
    }

Additionally, you need to adjust the scripts attribute.

    "scripts": {
    ...
    "predeploy": "npm run build",
    "deploy": "gh-pages -d build"
    },

Now you can run npm deploy and it will automatically create a branch, build the app and deploy it into your repository. It will even configure your github repository that the branch gh-pages will be now hosted.  So you can simply navigate to  https://<UserName>.github.io/<RepositoryName>/ and see the result. # Configure React-Router Before we can publish we probably have to change a couple of things in the configuration of the react-router.

  1. We need to set the basename. The basename ensures that all links of the Router are relative to this basename. To configure the basename you can use the variable process.env.PUBLIC_URL this ensures that you only need to configure the url via the package.json.
  2. We need to ensure that we are using aHashRouter instead of the typical BrowserRouter . The difference is that the hashRouter adds a ‘#’ into the url and upon a refresh of the page you will not see a 404 Github Error

Your index.js should look something like this:

    import * as React from 'react';
    import * as ReactDOM from 'react-dom';
    import { Router } from 'react-router';
    import createHashHistory from 'history/createHashHistory';
    
    const hashHistory = createHashHistory({ basename: process.env.PUBLIC_URL });
    
    ReactDOM.render(
        <Router history={hashHistory}>
          <App />
        </Router>,
      document.getElementById('root') as HTMLElement
    );

Deploy

Now can simply run `npm deploy` and your site should be published on https://<UserName>.github.io/<RepositoryName>/

Note: Since the build is published on a separate branch, your main repository may contain different src than what is published. So you still need to ensure that you commit your changes to your repository.

VSCode: Launch create-react-app and Chrome with launch.json

Developing React (with create-react-app) and Visual Studio Code you usually press ESC-` and then npm start. The script from create-react-app then automatically starts a Browser. That you then close. then reopen by pressing F5 to start Chrome in debug mode.

Let’s make these steps a little quicker.

Create a Task for npm start

Press Ctrl-Shift- and Select “Tasks: Configure Default Test Task” This will create a tasks.json file.

In the tasks.json file you need to add a couple of values:

  • isBackground: true – launch.json will not wait until the task completes
  • problemMatcher Needs to be defined to figure out when the task has completed its initialization phase and it is safe to continue with the next task
{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "type": "npm",
            "script": "start",
            "group": {
                "kind": "test",
                "isDefault": true
            },
            "isBackground": true,                       //This prevents the launch.json to wait for the completion of the task
            "problemMatcher": {
                "owner": "custom",                      //This is not needed but, required by the problemMatcher Object
                "pattern": {
                    "regexp": "^$"                      //This is not needed but, required by the problemMatcher Object
                },
                "background": {
                    "activeOnStart": true,
                    "beginsPattern": "Compiling...",    //Signals the begin of the Task
                    "endsPattern": "Compiled .*"        //Signals that now the initialization of the task is complete
                }
            },
            "identifier": "Start Server"
        }
    ]
}

Configure create-react-app

To prevent launching the browser you need to add in your .env-file following line:

BROWSER=none

More Info:

Configure the Launch.json file

Press F5 and select Chrome and a launch.json file will be created.

  • Change the port to 3000 (create-react-app default)
  • Add a preLaunchTask to start the task we defined earlier
{
    "version": "0.2.0",
    "configurations": \[ 
        {
            "name": "Chrome",
            "type": "chrome",
            "request": "launch",
            "url": "http://localhost:3000",         //Change to the create-react-app default 3000
            "webRoot": "${workspaceRoot}/src",
            "preLaunchTask": "npm: start"           //Add prelaunch Task npm: start (defined in tasks.json)
        }
    \]
}

Start Working

Tadaa, now you press F5 and can start debugging directly in vscode. The background task will continue running even when you stop debugging.

Stop Working

You need to terminate the task via ctrl-shift-p > terminate Task. (Or you just close vscode)

HTML 5: When to use or

tl:dr

  • <a> is used for page navigation
  • <button> is used for actions on the page
  • <input type="button" /> is used in a form and the value is used in the form

The Problem

Let’s take a look at this small piece of code:

<a id="btn" class="btn btn-default">Button</a>
<script>
  var btn = document.getElementById('btn');
  btn.addEventListener("click", function(e){
    e.preventDefault();
    console.log('awesome btn action');
});
</script>

Now half of the people I showed this piece of code, said: well there is nothing wrong with the code. That is a perfectly fine way of defining a button.

If you take a closer look, you will discover a couple of smaller issues with this piece of code.

First, you would notice that hovering over the button you get a text cursor instead of a ‘hand’-cursor. This is caused by leaving the attribute “href” undefined, you can fix it via defining something like ‘#’ or via CSS.

Then you would notice that by defining the “href” tag you would cause the browser to navigate to another page or the top of the page. To prevent this you would then need to add some Javascript code and call ‘event.preventDefault()’, or add to your previous hack and define the href as ‘#0’. This solves the problem by relying on the browser not knowing what to do with illegal element ids (ids can never start with a number).

Voila, you are now in the situation that you are using some hack pattern that degrades the readability of your code. Someone may come along and assume that someone made a mistake defining the href attribute /or used a placeholder and forgot to replace it.

The Solution

Now let’s improve the readability of the code:

<button id="btn" class="btn btn-default" type="button">Button</button>
<script>
  var btn = document.getElementById('btn');
  btn.addEventListener("click", function(){
    console.log('awesome btn action');
});
</script>

We are now using the correct tag <button>, no need for any hacks to fix the cursor or some wierd href, and no additional line of Javascript. You will need CSS to ensure that the button looks the same in all Browsers, however you would probably anyway used CSS to style your <a>-button.

But what about IE8? – for that we set the type=’button’, and IE8 is almost of no concern anymore (even for big corporate customers)

<button> vs <input type="button" />

Both types of buttons work in practice the same way. However, the Button element allows you to add content elements, like an image. And usually, you would use the <input type="text" /> tag in a </button>`.

As we are not working with a form and just want to have a button to execute some JS-Code I would use the Button tag.

Summary

  • <a> is used for page navigation
  • <button type="button"> is used for actions on the page, can contain other HTML elements
  • <input type="button" /> is used in a form

Some More Reading

Image: Designed by Freepik