Kukuh  Pratama

Kukuh Pratama

1586245680

Build a Progressive Web App (PWA) with Node.js

One way to visualize a progressive web app (PWA) is to compare it to web technologies that behave and feel like a mobile app. For example, most mobile apps have splash screens to notify the user that it’s loading, maintain some kind of functionality when offline, and work fast because most of the assets the user needs are already on the phone when they download an app.

In this tutorial, we’ll demonstrate how to build a PWA that has offline functionality and complies with all the Google Lighthouse checks.

Project setup

Before we start, let’s outline some requirements. Please note that these requirements are only for the purpose of this tutorial; you can mix, match, and swap any of them to suit your needs and goals.

For this project, you’ll need:
– Node.js to run the server
– Express to run the HTTP server
– Nodemon to debug the server
– Chrome to check the website and debug your PWA
– OpenSSL to generate a self-signed certificate (more on that later)

Folders

project-folder
  |_certs
  |_public
    |_images
    |_js

Files

project-folder
  |_certs
  |_public
    |_images
    |_js
      |_pwa.js
    |_index.html
  |_package.json
  |_server.js

package.json

Generate package.json with npm init and fill out the questions. Get the packages out of the way and proceed to npm install express nodemon. In package.json, add the script "server-debug": "nodemon --inspect server.js".

server.js

Create a basic HTTP server to generate your index.html once you connect to localhost in the browser.

const express = require('express')
const path = require('path')

const httpPort = 80

const app = express()

app.use(express.static(path.join(__dirname, 'public')))

app.get('/', function(req, res) {
  res.sendFile(path.join(__dirname, 'public/index.html'))
})

app.listen(httpPort, function () {
  console.log(`Listening on port ${httpPort}!`)
})

public/index.html

<html>
  <body>
    <span>This example is for the article of progressive web apps written for LogRocket</span>
    <br>
    <span>You are now</span> <span><b class="page-status">online</b></span>
    <script src="/js/pwa.js"></script>
  </body>
</html>

public/js/pwa.js

document.addEventListener('DOMContentLoaded', init, false);
function init() {
  console.log('empty for now')
}

In the browser, access [http://localhost](http://localhost) to see the page with just your initial message. Right-click→inspect to see your log on the console.

Building a PWA

Now that you have a basic page set up, how do you get Google to recognize it as a fully functional PWA?

Inspect again and select the audit tab, then Progressive Web App, and run the audit. You should end up with something like this:

Lighthouse Audit of a PWA

As you can see, most of the results are red. We’ll work on them until each one is green.

Some are already green because:

  • It takes less than 10 seconds to load the page
  • The page shows some text even when JavaScript is not available
  • Since we’re using localhost, the page is checked even if there’s no HTTPS

Adding a manifest

Adding a manifest will address the installability criterion as well as the missing splash screen.

public/js/pwa.webmanifest

{
  "name": "Progressive Web App example",
  "short_name": "pwa-tutorial",
  "description": "Progressive Web App example to be used in conjuction with the article in LogRocket",
  "icons": [
    {
      "src": "/../images/splash-screen.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "start_url": "/",
  "display": "fullscreen",
  "theme_color": "#764ABC",
  "background_color": "#764ABC"
}

public/index.html

<html>
  <head>
    <link rel="manifest" href="/js/pwa.webmanifest">
  </head>
  <body>
    <span>This example is for the article of progressive web apps written for LogRocket</span>
    <br>
    <span>You are now</span> <span><b class="page-status">online</b></span>
    <script src="/js/pwa.js"></script>
  </body>
</html>

public/images/splash-screen.png

You’ll also want to add a 512×512 image in the images folder. Call it splash-screen.png.

Splash Screen Image, 512x512

Now the red checks associated with the manifest and splash screen should be green.

This splash screen comes into play when the user opens the PWA on their phone, presses “Add to home screen,” and opens the PWA using the app that is downloaded on their phone.

Optimizing your PWA

Let’s keep chipping away at the easy checks, such as the theme color, an icon to show when the app is being used on an iOS device, and a viewport to make sure the application is responsive.

public/index.html

<html>
  <head>
    <link rel="manifest" href="/js/pwa.webmanifest">
    <link rel="apple-touch-icon" href="/images/apple-touch.png">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="theme-color" content="#764ABC"/>
  </head>
  <body>
    <span>This example is for the article of progressive web apps written for LogRocket</span>
    <br>
    <span>You are now</span> <span><b class="page-status">online</b></span>
    <script src="/js/pwa.js"></script>
  </body>
</html>

public/images/apple-touch.png

Below is the icon that is shown on iOS devices. It should be 192×192.

PWA iOS Icon

After making these changes, run Lighthouse again. You should see more green marks.

Lighthouse Audit of a PWA

There is still a red mark under PWA Optimized: all PWAs must be served with HTTPS. This requirement calls for using technology such as service workers to make sure the page is either localhost or HTTPS.

I usually get rid of that error by adding the SSL on the reverse proxy. That means I always have that criterion marked red locally, but for the purpose of this tutorial — just to get the satisfaction of seeing all green checks — we’ll generate a self-signed certificate and change the server to redirect to HTTPS.

To generate the self-signed certificates, go to the certs folder and run the following on the command line.

openssl req -x509 -out localhost.crt -keyout localhost.key \
  -newkey rsa:2048 -nodes -sha256 \
  -subj '/CN=localhost' -extensions EXT -config <( \
   printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")

This should create your missing files. Now you can update your server.

server.js

const express = require('express')
const path = require('path')
const fs = require('fs')
const https = require('https')

const httpPort = 80
const httpsPort = 443
const key = fs.readFileSync('./certs/localhost.key');
const cert = fs.readFileSync('./certs/localhost.crt');

const app = express()
const server = https.createServer({key: key, cert: cert }, app);

app.use((req, res, next) => {
  if (!req.secure) {
    return res.redirect('https://' + req.headers.host + req.url);
  }
  next();
})

app.use(express.static(path.join(__dirname, 'public')))

app.get('/', function(req, res) {
  res.sendFile(path.join(__dirname, 'public/index.html'))
})

app.listen(httpPort, function () {
  console.log(`Listening on port ${httpPort}!`)
})

server.listen(httpsPort, function () {
  console.log(`Listening on port ${httpsPort}!`)
})

What we’re doing is creating a server on port 80 (HTTP) and a server on port 443 (HTTPS). Whenever you try to access localhost with http://localhost, the HTTP is triggered and the middleware checks whether the connection (HTTPS) is secure. If it’s not, then it redirects as intended.

The next step is to make the application work even if the connection is lost. For that, we’ll use service workers.

Service workers

A service worker is a piece of JavaScript code that handles the cache for assets and data you choose to save for future requests.

A service worker has some rules you must follow to make it work:

- It only works with valid HTTPS or http://localhost
- It only grabs requests within its scope
- It only has access to the files on its folder or “below”

To expand on the scope, imagine the following structure.

/public
  |_drinks
    |_drinks-service-worker.js
    |_drinks.js
    |_coffee
      |_coffee.js
      |_coffee-service-worker.js
    |_tea
      |_tea.js
      |_tea-service-worker.js

For this example, both tea and coffee service workers will only trigger if a call is made for files in their respective folders, such as tea.js or coffee.js. On the other hand, the drinks service worker will be triggered regardless of what you call; its scope is everything in its folder and “below.”

Since it’s a worker, it doesn’t have access to the DOM — meaning that, inside a service worker file, you can’t access anything with, for example, document.querySelector.

To register your worker, first check whether the browser is compatible. If it is, add the registration and error functions.

public/js/pwa.js

document.addEventListener('DOMContentLoaded', init, false);
function init() {
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/service-worker.js')
      .then((reg) => {
        console.log('Service worker registered -->', reg);
      }, (err) => {
        console.error('Service worker not registered -->', err);
      });
  }
}

public/service-worker.js

self.addEventListener('install', function(event) {
  console.log('used to register the service worker')
})

self.addEventListener('fetch', function(event) {
  console.log('used to intercept requests so we can check for the file or data in the cache')
})

self.addEventListener('activate', function(event) {
  console.log('this event triggers when the service worker activates')
})

You won’t need the other events for your service worker, but for good measure, they are message, sync, and push.

Since install is the first thing that is triggered when you try to register an SW, change your event to the following and specify the files you want to save in your cache.

const CACHE_NAME = 'sw-cache-example';
const toCache = [
  '/',
  '/index.html',
];

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        return cache.addAll(toCache)
      })
      .then(self.skipWaiting())
  )
})

Add skipWaiting for when you update the SW to avoid the need for the user to navigate away from the page.

To see your service worker, inspect the page again. In Chrome DevTools, in the application tab, you can see the current status of your service worker, set the page to offline to test it out (spoiler alert: it won’t do anything yet). check the current cache, and clear everything if you want to restart.

You might recall that the service worker requires a valid HTTPS certificate. As a result, you may find yourself with the following error.

Service Worker Registration Error

One way to avoid this is to run Chrome via the command line with a flag for https://localhost.

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --user-data-dir=/tmp/foo --ignore-certificate-errors --unsafely-treat-insecure-origin-as-secure=https://localhost

This should generate a more pleasant register.

Service Worker Registered

Whenever we update our service worker, we want the old ones to be removed instead of leaving them hanging in the client’s browser. Go to your activate event and change it to the following.

self.addEventListener('activate', function(event) {
  event.waitUntil(
    caches.keys()
      .then((keyList) => {
        return Promise.all(keyList.map((key) => {
          if (key !== CACHE_NAME) {
            console.log('[ServiceWorker] Removing old cache', key)
            return caches.delete(key)
          }
        }))
      })
      .then(() => self.clients.claim())
  )
})

This ensures that the old service workers are removed and claims your newly installed SW as the one to use from now on.

That leaves us with the fetch. We haven’t used our cached files at all, so it’s time to update our fetch event.

self.addEventListener('fetch', function(event) {
  event.respondWith(
    fetch(event.request)
      .catch(() => {
        return caches.open(CACHE_NAME)
          .then((cache) => {
            return cache.match(event.request)
          })
      })
  )
})

This checks every request that is made on the page. If there is a match found in the cache — take localhost/, for example, since we have it cached — it will use the cached version.

In this case, the / is the index.html file, which will include other resources, such as /js/pwa.js. This is not in your cache, so a normal request will be made to the server to fetch the dependencies of that file.

We can store as much as we want in the cache, but bear in mind that each browser has a limit that widely varies. In my experience, the safe value is not more than 50MB, which, on the web, is quite a bit.

With all three events done, it’s time to create a file to change the page state from online to offline whenever the client loses connection and is using purely service workers.

public/js/status.js

document.addEventListener('DOMContentLoaded', init, false);

function init() {
  if (!navigator.onLine) {
    const statusElem = document.querySelector('.page-status')
    statusElem.innerHTML = 'offline'
  }
}

public/index.html

<html>
  <head>
    <link rel="manifest" href="/js/pwa.webmanifest">
    <link rel="apple-touch-icon" href="/images/apple-touch.png">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="theme-color" content="#764ABC"/>
  </head>
  <body>
    <span>This in an examples for here article of progressive web apps that can be found in LogRocket</span>
    <br>
    <span>You are now</span> <span><b class="page-status">online</b></span>
    <script src="/js/pwa.js"></script>
    <script src="/js/status.js"></script>
  </body>
</html>

public/service-worker.js

const CACHE_NAME = 'sw-cache-example';
const toCache = [
  '/',
  '/index.html',
  '/js/status.js',
];

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        return cache.addAll(toCache)
      })
      .then(self.skipWaiting())
  )
})

self.addEventListener('fetch', function(event) {
  event.respondWith(
    fetch(event.request)
      .catch(() => {
        return caches.open(CACHE_NAME)
          .then((cache) => {
            return cache.match(event.request)
          })
      })
  )
})

self.addEventListener('activate', function(event) {
  event.waitUntil(
    caches.keys()
      .then((keyList) => {
        return Promise.all(keyList.map((key) => {
          if (key !== CACHE_NAME) {
            console.log('[ServiceWorker] Removing old cache', key)
            return caches.delete(key)
          }
        }))
      })
      .then(() => self.clients.claim())
  )
})

The code above adds a status file to check whether the browser is online and, if not, changes to offline. We included the file in both the index.html and the cache of our SW so it can be used offline.

To try it all out, reload the page and, on the DevTools application tab, view your files in the cache.

Viewing a PWA in Google DevTools

If the connection is working, you should see this:

Testing the Connection in a PWA

If you select the offline option, you should see the status change.

Changing to Offline Status in PWA

You should see some errors on the console since we didn’t add the manifest and other files that index.html requires — which won’t affect anything in offline mode, but if it’s bothersome, all you need to do is add them to the cache.

Finally, to make sure everything is green in Lighthouse, run it the app without the offline check. You should get a result similar to this:

Lighthouse PWA Score of 100

Congratulations — you’ve just built your first PWA with all criteria checked in Lighthouse!

Compatibility

Compatibility is all over the place with PWAs since we’re not talking about just one technology, but an ecosystem of elements such as service workers, web app manifest, notification, push, and add to home screen, each of which have completely different compatibilities across the board.

That said, service workers generally have very good support. On the other hand, web app manifest, which doesn’t work for Firefox or Safari at the moment of writing, is much less compatible.

Always check for polyfills and, in case there aren’t any, make sure you have a fallback for when the browser doesn’t have that technology implemented.

Pros and cons of PWAs

Companies such as Twitter and Alibaba have improved engagement by switching to PWAs, among many others who have made the switch.

Based on my experience and personal reading, below is a short list of advantages and disadvantages associated with PWAs.

On the pro side, PWAs:

  • Are fast after the first visit to the website since a lot of assets are cached
  • Are easy to implement gradually on an existing website
  • Use less data, again, since many assets are cached
  • Are independent technologies, meaning you’re not bound to a library that comes with 10 technologies when you only need one or two. For example, you can use service workers without using notifications

Some drawbacks:

  • The first visit to a page will be slow since it must download the files and data to the cache
  • Add to home screen functionality is seldom used, at least to my knowledge
  • Support between browsers varies widely depending on what technology you’re using. For example, service workers have good support but web app manifest does not, so it can be tricky to identify what you can use safely out of the box and what requires a polyfill from the start

Conclusion

Is it worth it to implement PWA technology? I would say yes. A website, even when offline, should have accessible information. If you run a news website, for example, you might give your users the option to make their favorite news available offline or notify them when something of interest happens, like a promotion on on item in their shopping cart.

What do you think of PWAs? Do you prefer a more native approach? Have you built anything with it?

You can reference the code used in this tutorial on GitHub.

Originally published by Diogo Spínola at https://blog.logrocket.com

#node-js #javascript #web-development #pwa

What is GEEK

Buddha Community

Build a Progressive Web App (PWA) with Node.js

Top Progressive Web App Development Company in USA

AppClues Infotech is one of the leading progressive web app development company in USA. We offer the best progressive web app development & design solution to create high-performance & secure PWA.

For more info:
Website: https://www.appcluesinfotech.com/
Email: info@appcluesinfotech.com
Call: +1-978-309-9910

#top progressive web app development company in usa #hire progressive web app developers in usa #best pwa development company in usa #custom progressive web app development company #progressive web apps development #progressive web app development services

Hunter  Krajcik

Hunter Krajcik

1674813120

Validate Multi Step Form Using jQuery

In this article, we will see how to validate multi step form wizard using jquery. Here, we will learn to validate the multi step form using jquery. First, we create the multi step form using bootstrap. Also, in this example, we are not using any jquery plugin for multi step form wizard.

So, let's see jquery multi step form with validation, how to create multi step form, multi step form wizard with jquery validation, bootstrap 4 multi step form wizard with validation, multi step form bootstrap 5, and jQuery multi step form with validation and next previous navigation.

Add HTML:

<html lang="en">
</head>
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700&display=swap" rel="stylesheet">
</head>
<body>  
    <div class="main">
      <h3>How To Validate Multi Step Form Using jQuery - Websolutionstuff</h3>
        <form id="multistep_form">
            <!-- progressbar -->
            <ul id="progress_header">
                <li class="active"></li>
                <li></li>
                <li></li>
            </ul>
            <!-- Step 01 -->
            <div class="multistep-box">
                <div class="title-box">
                    <h2>Create your account</h2>
                </div>
                <p>
                    <input type="text" name="email" placeholder="Email" id="email">
                    <span id="error-email"></span>
                </p>
                <p>
                    <input type="password" name="pass" placeholder="Password" id="pass">
                    <span id="error-pass"></span>
                </p>
                <p>
                    <input type="password" name="cpass" placeholder="Confirm Password" id="cpass">
                    <span id="error-cpass"></span>
                </p>
                <p class="nxt-prev-button"><input type="button" name="next" class="fs_next_btn action-button" value="Next" /></p>
            </div>
            <!-- Step 02 -->
            <div class="multistep-box">
                <div class="title-box">
                    <h2>Social Profiles</h2>
                </div>
                <p>
                    <input type="text" name="twitter" placeholder="Twitter" id="twitter">
                    <span id="error-twitter"></span>
                </p>
                <p>
                    <input type="text" name="facebook" placeholder="Facebook" id="facebook">
                    <span id="error-facebook"></span>
                </p>
                <p>
                    <input type="text" name="linkedin" placeholder="Linkedin" id="linkedin">
                    <span id="error-linkedin"></span>
                </p>
                <p class="nxt-prev-button">
                    <input type="button" name="previous" class="previous action-button" value="Previous" />
                    <input type="button" name="next" class="ss_next_btn action-button" value="Next" />
                </p>
            </div>
            <!-- Step 03 -->
            <div class="multistep-box">
                <div class="title-box">
                    <h2>Personal Details</h2>
                </div>
                <p>
                    <input type="text" name="fname" placeholder="First Name" id="fname">
                    <span id="error-fname"></span>
                </p>
                <p>
                    <input type="text" name="lname" placeholder="Last Name" id="lname">
                    <span id="error-lname"></span>
                </p>
                <p>
                    <input type="text" name="phone" placeholder="Phone" id="phone">
                    <span id="error-phone"></span>
                </p>
                <p>
                    <textarea name="address" placeholder="Address" id="address"></textarea>
                    <span id="error-address"></span>
                </p>
                <p class="nxt-prev-button"><input type="button" name="previous" class="previous action-button" value="Previous" />
                    <input type="submit" name="submit" class="submit_btn ts_next_btn action-button" value="Submit" />
                </p>
            </div>
        </form>
        <h1>You are successfully logged in</h1>
    </div>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.0/jquery.easing.js" type="text/javascript"></script>
</body>
</html>

Add CSS:

body {
    display: inline-block;
    width: 100%;
    height: 100vh;
    overflow: hidden;
    background-image: url("https://img1.akspic.com/image/80377-gadget-numeric_keypad-input_device-electronic_device-space_bar-3840x2160.jpg");
    background-repeat: no-repeat;
    background-size: cover;
    position: relative;
    margin: 0;
    font-weight: 400;
    font-family: 'Roboto', sans-serif;
}
body:before {
    content: "";
    display: block;
    width: 100%;
    height: 100%;
    background: rgba(0,0,0,0.5);
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
}
.main {
    position: absolute;
    left: 0;
    right: 0;
    top: 30px;
    margin: 0 auto;
    height: 515px;
}
input:-internal-autofill-selected {
    background-color: #fff !important;
}
#multistep_form {
    width: 550px;
    margin: 0 auto;
    text-align: center;
    position: relative;
    height: 100%;
    z-index: 999;
    opacity: 1; 
    visibility: visible; 
}
/*progress header*/
#progress_header {
    overflow: hidden;
    margin: 0 auto 30px;
    padding: 0;
}
#progress_header li {
    list-style-type: none;
    width: 33.33%;
    float: left;
    position: relative;
    font-size: 16px;
    font-weight: bold;
    font-family: monospace;
    color: #fff;
    text-transform: uppercase;
}
#progress_header li:after {
    width: 35px;
    line-height: 35px;
    display: block;
    font-size: 22px;
    color: #888;
    font-family: monospace;
    background-color: #fff;
    border-radius: 100px;
    margin: 0 auto;
    background-repeat: no-repeat;
    font-family: 'Roboto', sans-serif;
}
#progress_header li:nth-child(1):after {
    content: "1";
}
#progress_header li:nth-child(2):after {
    content: "2";
}
#progress_header li:nth-child(3):after {
    content: "3";
}
#progress_header li:before {
    content: '';
    width: 100%;
    height: 5px;
    background: #fff;
    position: absolute;
    left: -50%;
    top: 50%;
    z-index: -1;
}
#progress_header li:first-child:before {
    content: none;
}
#progress_header li.active:before, 
#progress_header li.active:after {
    background-image: linear-gradient(to right top, #35e8c3, #36edbb, #3df2b2, #4af7a7, #59fb9b) !important;
    color: #fff !important;
    transition: all 0.5s;
}
/*title*/
.title-box {
    width: 100%;
    margin: 0 0 30px 0;
}
.title-box h2 {
    font-size: 22px;
    text-transform: uppercase;
    color: #2C3E50;
    margin: 0;
    font-family: cursive;
    display: inline-block;
    position: relative;
    padding: 0 0 10px 0;
    font-family: 'Roboto', sans-serif;
}
.title-box h2:before {
    content: "";
    background: #6ddc8b;
    width: 70px;
    height: 2px;
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    margin: 0 auto;
    display: block;
}
.title-box h2:after {
    content: "";
    background: #6ddc8b;
    width: 50px;
    height: 2px;
    position: absolute;
    bottom: -5px;
    left: 0;
    right: 0;
    margin: 0 auto;
    display: block;
}
/*Input and Button*/
.multistep-box {
    background: white;
    border: 0 none;
    border-radius: 3px;
    box-shadow: 1px 1px 55px 3px rgba(255, 255, 255, 0.4);
    padding: 30px 30px;
    box-sizing: border-box;
    width: 80%;
    margin: 0 10%;
    position: absolute;
}
.multistep-box:not(:first-of-type) {
    display: none;
}
.multistep-box p {
    margin: 0 0 12px 0;
    text-align: left;
}
.multistep-box span {
    font-size: 12px;
    color: #FF0000;
}
input, textarea {
    padding: 15px;
    border: 1px solid #ccc;
    border-radius: 3px;
    margin: 0;
    width: 100%;
    box-sizing: border-box;
    font-family: 'Roboto', sans-serif;
    color: #2C3E50;
    font-size: 13px;
    transition: all 0.5s;
    outline: none;
}
input:focus, textarea:focus {
    box-shadow: inset 0px 0px 50px 2px rgb(0,0,0,0.1);
}
input.box_error, textarea.box_error {
    border-color: #FF0000;
    box-shadow: inset 0px 0px 50px 2px rgb(255,0,0,0.1);
}
input.box_error:focus, textarea.box_error:focus {
    box-shadow: inset 0px 0px 50px 2px rgb(255,0,0,0.1);
}
p.nxt-prev-button {
    margin: 25px 0 0 0;
    text-align: center;
}
.action-button {
    width: 100px;
    font-weight: bold;
    color: white;
    border: 0 none;
    border-radius: 1px;
    cursor: pointer;
    padding: 10px 5px;
    margin: 0 5px;
    background-image: linear-gradient(to right top, #35e8c3, #36edbb, #3df2b2, #4af7a7, #59fb9b);
    transition: all 0.5s;
}
.action-button:hover, 
.action-button:focus {
    box-shadow: 0 0 0 2px white, 0 0 0 3px #6ce199;
}
.form_submited #multistep_form {
    opacity: 0; 
    visibility: hidden; 
}
.form_submited h1 {
    -webkit-background-clip: text;
    transform: translate(0%, 0%);
    -webkit-transform: translate(0%, 0%);
    transition: all 0.3s ease;
    opacity: 1;
    visibility: visible;
}
h1 {
    margin: 0;
    text-align: center;
    font-size: 90px;
    background-image: linear-gradient(to right top, #35e8c3, #36edbb, #3df2b2, #4af7a7, #59fb9b) !important;
    background-image: linear-gradient(to right top, #35e8c3, #36edbb, #3df2b2, #4af7a7, #59fb9b) !important;
    color: transparent;
    -webkit-background-clip: text;
    -webkit-background-clip: text;
    transform: translate(0%, -80%);
    -webkit-transform: translate(0%, -80%);
    transition: all 0.3s ease;
    opacity: 0;
    visibility: hidden;
    position: absolute;
    left: 0;
    right: 0;
    margin: 0 auto;
    text-align: center;
    top: 50%;
}
h3{
  color:#fff;
  text-align:center;
  margin-bottom:20px;
}

Add jQuery:

var current_slide, next_slide, previous_slide;
var left, opacity, scale;
var animation;

var error = false;

// email validation
$("#email").keyup(function() {
    var emailReg = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/;
    if (!emailReg.test($("#email").val())) {
        $("#error-email").text('Please enter an Email addres.');
        $("#email").addClass("box_error");
        error = true;
    } else {
        $("#error-email").text('');
        error = false;
        $("#email").removeClass("box_error");
    }
});
// password validation
$("#pass").keyup(function() {
    var pass = $("#pass").val();
    var cpass = $("#cpass").val();

    if (pass != '') {
        $("#error-pass").text('');
        error = false;
        $("#pass").removeClass("box_error");
    }
    if (pass != cpass && cpass != '') {
        $("#error-cpass").text('Password and Confirm Password is not matched.');
        error = true;
    } else {
        $("#error-cpass").text('');
        error = false;
    }
});
// confirm password validation
$("#cpass").keyup(function() {
    var pass = $("#pass").val();
    var cpass = $("#cpass").val();

    if (pass != cpass) {
        $("#error-cpass").text('Please enter the same Password as above.');
        $("#cpass").addClass("box_error");
        error = true;
    } else {
        $("#error-cpass").text('');
        error = false;
        $("#cpass").removeClass("box_error");
    }
});
// twitter
$("#twitter").keyup(function() {
    var twitterReg = /https?:\/\/twitter\.com\/(#!\/)?[a-z0-9_]+$/;
    if (!twitterReg.test($("#twitter").val())) {
        $("#error-twitter").text('Twitter link is not valid.');
        $("#twitter").addClass("box_error");
        error = true;
    } else {
        $("#error-twitter").text('');
        error = false;
        $("#twitter").removeClass("box_error");
    }
});
// facebook
$("#facebook").keyup(function() {
    var facebookReg = /^(https?:\/\/)?(www\.)?facebook.com\/[a-zA-Z0-9(\.\?)?]/;
    if (!facebookReg.test($("#facebook").val())) {
        $("#error-facebook").text('Facebook link is not valid.');
        $("#facebook").addClass("box_error");
        error = true;
    } else {
        $("#error-facebook").text('');
        error = false;
        $("#facebook").removeClass("box_error");
    }
});
// linkedin
$("#linkedin").keyup(function() {
    var linkedinReg = /(ftp|http|https):\/\/?(?:www\.)?linkedin.com(\w+:{0,1}\w*@)?(\S+)(:([0-9])+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
    if (!linkedinReg.test($("#linkedin").val())) {
        $("#error-linkedin").text('Linkedin link is not valid.');
        $("#linkedin").addClass("box_error");
        error = true;
    } else {
        $("#error-linkedin").text('');
        error = false;
        $("#linkedin").removeClass("box_error");
    }
});
// first name
$("#fname").keyup(function() {
    var fname = $("#fname").val();
    if (fname == '') {
        $("#error-fname").text('Enter your First name.');
        $("#fname").addClass("box_error");
        error = true;
    } else {
        $("#error-fname").text('');
        error = false;
    }
    if ((fname.length <= 2) || (fname.length > 20)) {
        $("#error-fname").text("User length must be between 2 and 20 Characters.");
        $("#fname").addClass("box_error");
        error = true;
    }
    if (!isNaN(fname)) {
        $("#error-fname").text("Only Characters are allowed.");
        $("#fname").addClass("box_error");
        error = true;
    } else {
        $("#fname").removeClass("box_error");
    }
});
// last name
$("#lname").keyup(function() {
    var lname = $("#lname").val();
    if (lname != lname) {
        $("#error-lname").text('Enter your Last name.');
        $("#lname").addClass("box_error");
        error = true;
    } else {
        $("#error-lname").text('');
        error = false;
    }
    if ((lname.length <= 2) || (lname.length > 20)) {
        $("#error-lname").text("User length must be between 2 and 20 Characters.");
        $("#lname").addClass("box_error");
        error = true;
    }
    if (!isNaN(lname)) {
        $("#error-lname").text("Only Characters are allowed.");
        $("#lname").addClass("box_error");
        error = true;
    } else {
        $("#lname").removeClass("box_error");
    }
});
// phone
$("#phone").keyup(function() {
    var phone = $("#phone").val();
    if (phone != phone) {
        $("#error-phone").text('Enter your Phone number.');
        $("#phone").addClass("box_error");
        error = true;
    } else {
        $("#error-phone").text('');
        error = false;
    }
    if (phone.length != 10) {
        $("#error-phone").text("Mobile number must be of 10 Digits only.");
        $("#phone").addClass("box_error");
        error = true;
    } else {
        $("#phone").removeClass("box_error");
    }
});
// address
$("#address").keyup(function() {
    var address = $("#address").val();
    if (address != address) {
        $("#error-address").text('Enter your Address.');
        $("#address").addClass("box_error");
        error = true;
    } else {
        $("#error-address").text('');
        error = false;
        $("#address").removeClass("box_error");
    }
});

// first step validation
$(".fs_next_btn").click(function() {
    // email
    if ($("#email").val() == '') {
        $("#error-email").text('Please enter an email address.');
        $("#email").addClass("box_error");
        error = true;
    } else {
        var emailReg = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/;
        if (!emailReg.test($("#email").val())) {
            $("#error-email").text('Please insert a valid email address.');
            error = true;
        } else {
            $("#error-email").text('');
            $("#email").removeClass("box_error");
        }
    }
    // password
    if ($("#pass").val() == '') {
        $("#error-pass").text('Please enter a password.');
        $("#pass").addClass("box_error");
        error = true;
    }
    if ($("#cpass").val() == '') {
        $("#error-cpass").text('Please enter a Confirm password.');
        $("#cpass").addClass("box_error");
        error = true;
    } else {
        var pass = $("#pass").val();
        var cpass = $("#cpass").val();

        if (pass != cpass) {
            $("#error-cpass").text('Please enter the same password as above.');
            error = true;
        } else {
            $("#error-cpass").text('');
            $("#pass").removeClass("box_error");
            $("#cpass").removeClass("box_error");
        }
    }
    // animation
    if (!error) {
        if (animation) return false;
        animation = true;

        current_slide = $(this).parent().parent();
        next_slide = $(this).parent().parent().next();

        $("#progress_header li").eq($(".multistep-box").index(next_slide)).addClass("active");

        next_slide.show();
        current_slide.animate({
            opacity: 0
        }, {
            step: function(now, mx) {
                scale = 1 - (1 - now) * 0.2;
                left = (now * 50) + "%";
                opacity = 1 - now;
                current_slide.css({
                    'transform': 'scale(' + scale + ')'
                });
                next_slide.css({
                    'left': left,
                    'opacity': opacity
                });
            },
            duration: 800,
            complete: function() {
                current_slide.hide();
                animation = false;
            },
            easing: 'easeInOutBack'
        });
    }
});
// second step validation
$(".ss_next_btn").click(function() {
    // twitter
    if ($("#twitter").val() == '') {
        $("#error-twitter").text('twitter link is required.');
        $("#twitter").addClass("box_error");
        error = true;
    } else {
        var twitterReg = /https?:\/\/twitter\.com\/(#!\/)?[a-z0-9_]+$/;
        if (!twitterReg.test($("#twitter").val())) {
            $("#error-twitter").text('Twitter link is not valid.');
            error = true;
        } else {
            $("#error-twitter").text('');
            $("#twitter").removeClass("box_error");
        }
    }
    // facebook
    if ($("#facebook").val() == '') {
        $("#error-facebook").text('Facebook link is required.');
        $("#facebook").addClass("box_error");
        error = true;
    } else {
        var facebookReg = /^(https?:\/\/)?(www\.)?facebook.com\/[a-zA-Z0-9(\.\?)?]/;
        if (!facebookReg.test($("#facebook").val())) {
            $("#error-facebook").text('Facebook link is not valid.');
            error = true;
            $("#facebook").addClass("box_error");
        } else {
            $("#error-facebook").text('');
        }
    }
    // linkedin
    if ($("#linkedin").val() == '') {
        $("#error-linkedin").text('Linkedin link is required.');
        $("#linkedin").addClass("box_error");
        error = true;
    } else {
        var linkedinReg = /(ftp|http|https):\/\/?(?:www\.)?linkedin.com(\w+:{0,1}\w*@)?(\S+)(:([0-9])+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
        if (!linkedinReg.test($("#linkedin").val())) {
            $("#error-linkedin").text('Linkedin link is not valid.');
            error = true;
        } else {
            $("#error-linkedin").text('');
            $("#linkedin").removeClass("box_error");
        }
    }

    if (!error) {
        if (animation) return false;
        animation = true;

        current_slide = $(this).parent().parent();
        next_slide = $(this).parent().parent().next();

        $("#progress_header li").eq($(".multistep-box").index(next_slide)).addClass("active");

        next_slide.show();
        current_slide.animate({
            opacity: 0
        }, {
            step: function(now, mx) {
                scale = 1 - (1 - now) * 0.2;
                left = (now * 50) + "%";
                opacity = 1 - now;
                current_slide.css({
                    'transform': 'scale(' + scale + ')'
                });
                next_slide.css({
                    'left': left,
                    'opacity': opacity
                });
            },
            duration: 800,
            complete: function() {
                current_slide.hide();
                animation = false;
            },
            easing: 'easeInOutBack'
        });
    }

});

// third step validation
$(".ts_next_btn").click(function() {
    // first name
    if ($("#fname").val() == '') {
        $("#error-fname").text('Enter your First name.');
        $("#fname").addClass("box_error");
        error = true;
    } else {
        var fname = $("#fname").val();
        if (fname != fname) {
            $("#error-fname").text('First name is required.');
            error = true;
        } else {
            $("#error-fname").text('');
            error = false;
            $("#fname").removeClass("box_error");
        }
        if ((fname.length <= 2) || (fname.length > 20)) {
            $("#error-fname").text("User length must be between 2 and 20 Characters.");
            error = true;
        }
        if (!isNaN(fname)) {
            $("#error-fname").text("Only Characters are allowed.");
            error = true;
        } else {
            $("#fname").removeClass("box_error");
        }
    }
    // last name
    if ($("#lname").val() == '') {
        $("#error-lname").text('Enter your Last name.');
        $("#lname").addClass("box_error");
        error = true;
    } else {
        var lname = $("#lname").val();
        if (lname != lname) {
            $("#error-lname").text('Last name is required.');
            error = true;
        } else {
            $("#error-lname").text('');
            error = false;
        }
        if ((lname.length <= 2) || (lname.length > 20)) {
            $("#error-lname").text("User length must be between 2 and 20 Characters.");
            error = true;
        } 
        if (!isNaN(lname)) {
            $("#error-lname").text("Only Characters are allowed.");
            error = true;
        } else {
            $("#lname").removeClass("box_error");
        }
    }
    // phone
    if ($("#phone").val() == '') {
        $("#error-phone").text('Enter your Phone number.');
        $("#phone").addClass("box_error");
        error = true;
    } else {
        var phone = $("#phone").val();
        if (phone != phone) {
            $("#error-phone").text('Phone number is required.');
            error = true;
        } else {
            $("#error-phone").text('');
            error = false;
        }
        if (phone.length != 10) {
            $("#error-phone").text("Mobile number must be of 10 Digits only.");
            error = true;
        } else {
            $("#phone").removeClass("box_error");
        }
    }
    // address
    if ($("#address").val() == '') {
        $("#error-address").text('Enter your Address.');
        $("#address").addClass("box_error");
        error = true;
    } else {
        var address = $("#address").val();
        if (address != address) {
            $("#error-address").text('Address is required.');
            error = true;
        } else {
            $("#error-address").text('');
            $("#address").removeClass("box_error");
            error = false;
        }
    }

    if (!error) {
        if (animation) return false;
        animation = true;

        current_slide = $(this).parent().parent();
        next_slide = $(this).parent().parent().next();

        $("#progress_header li").eq($(".multistep-box").index(next_slide)).addClass("active");

        next_slide.show();
        current_slide.animate({
            opacity: 0
        }, {
            step: function(now, mx) {
                scale = 1 - (1 - now) * 0.2;
                left = (now * 50) + "%";
                opacity = 1 - now;
                current_slide.css({
                    'transform': 'scale(' + scale + ')'
                });
                next_slide.css({
                    'left': left,
                    'opacity': opacity
                });
            },
            duration: 800,
            complete: function() {
                current_slide.hide();
                animation = false;
            },
            easing: 'easeInOutBack'
        });
    }
});
// previous
$(".previous").click(function() {
    if (animation) return false;
    animation = true;

    current_slide = $(this).parent().parent();
    previous_slide = $(this).parent().parent().prev();

    $("#progress_header li").eq($(".multistep-box").index(current_slide)).removeClass("active");

    previous_slide.show();
    current_slide.animate({
        opacity: 0
    }, {
        step: function(now, mx) {
            scale = 0.8 + (1 - now) * 0.2;
            left = ((1 - now) * 50) + "%";
            opacity = 1 - now;
            current_slide.css({
                'left': left
            });
            previous_slide.css({
                'transform': 'scale(' + scale + ')',
                'opacity': opacity
            });
        },
        duration: 800,
        complete: function() {
            current_slide.hide();
            animation = false;
        },
        easing: 'easeInOutBack'
    });
});

$(".submit_btn").click(function() {
    if (!error){
        $(".main").addClass("form_submited");
    }
    return false;
})

 

Output:

how_to_validate_multi_step_form_using_jquery_output

Original article source at: https://websolutionstuff.com/

#jquery #validate #step 

Rahim Makhani

Rahim Makhani

1625717787

Reach out to more customers: get a progressive web app

A Progressive Web App is a type of application software that is delivered through the web. It is built using standard web technologies HTML, CSS, and JavaScript. It is expected to work on any platform that uses a standards-compliant browser, including desktop and mobile.

It is also known as a type of web page or web server. With the help of these PWA web pages, you can reach more customers for a business by marketing and promoting your business through web pages. You can also gain more customers by developing PWA by hiring a progressive web app development company. Nevina Infotech is a company that can help you to build your progressive web app with the help of its enthusiastic developers.

#progressive web app development company #progressive web app developers #progressive web app platform #progressive web app development services #progressive web app development

Rahim Makhani

Rahim Makhani

1626327771

Get your custom progressive web app with the latest features

Suppose you are looking for an app that can be run on any platform, whether on a desktop or a smartphone. Progressive web app development can provide this functionality to your app. It’s made using the most common web technologies, including HTML, CSS, and JavaScript.

To get a PWA for your business requirements, you can find a progressive web app development company that can understand your vision and give you the best app that you deserve. Nevina Infotech is one of the best PWA development companies, which have the most dedicated developers with the experience of app development that can help you achieve your goals.

#progressive web app development company #progressive web app developers #progressive web app platform #progressive web app development services #progressive web app development

Rahim Makhani

Rahim Makhani

1620187126

Get your Custom Progressive Web App at an affordable Price

Progressive Web App or PWA is a type of application software that is delivered over the web. It is built using standard web technologies, including HTML, CSS, and JavaScript. It is designed to work on any platform that uses a standards-compliant browser. It can work on both desktop and mobile devices.

PWA is a type of website or web page known as a web application. It doesn’t require separate bundling or distribution.

Are you searching for a progressive web app development company to develop your custom PWA at an affordable rate? Then Nevina Infotech is the best suitable choice for you. We have experienced and dedicated developers who will help you to develop your custom PWA as per your requirement.

#progressive web app development company #progressive web app developers #progressive web app platform #progressive web app development services #progressive web app development