Develop a Flight Booking App Using HTML, CSS, and JavaScript (Source Code)

Faraz

By Faraz -

Learn how to build a flight booking app from scratch using HTML, CSS, and JavaScript. Source code included for easy implementation.


Develop a Flight Booking App Using HTML, CSS, and JavaScript.webp

Table of Contents

  1. Project Introduction
  2. HTML Code
  3. CSS Code
  4. JavaScript Code
  5. Preview
  6. Conclusion

Building a flight booking app from scratch can be an exciting and rewarding journey for developers. In this digital era, where online services have become indispensable, creating a flight booking app using HTML, CSS, and JavaScript opens up a world of possibilities. Whether you're a novice looking to delve into web development or an experienced developer seeking a new project, this guide will equip you with the knowledge and skills to embark on this endeavor.

Throughout this tutorial, we'll break down the process step by step, guiding you through setting up your development environment, designing the user interface, and implementing functionality. By the end of this journey, you'll not only have a functional flight booking app but also a deeper understanding of front-end development principles and best practices.

So, without further ado, let's embark on this exciting journey of creating your very own flight booking app using the power of HTML, CSS, and JavaScript.

Source Code

Step 1 (HTML Code):

To get started, we will first need to create a basic HTML file. In this file, we will include the main structure for our flight booking app.

After creating the files just paste the following codes into your file. Make sure to save your HTML document with a .html extension, so that it can be properly viewed in a web browser.

Here's a breakdown of the HTML code:

1. <!DOCTYPE html>: Declares the document type and version of HTML being used.

2. <html lang="en">: Defines the root element of the HTML document and specifies the language as English.

3. <head>: Contains meta-information about the HTML document and links to external resources.

  • <meta charset="UTF-8">: Specifies the character encoding for the document as UTF-8.
  • <meta http-equiv="X-UA-Compatible" content="IE=edge">: Provides instructions to the browser on how to render the webpage.
  • <meta name="viewport" content="width=device-width, initial-scale=1.0">: Sets the viewport to the width of the device and initial scale to 1.
  • <title>Flight Booking App</title>: Sets the title of the webpage.
  • <link rel="stylesheet" href="styles.css">: Links an external CSS file named "styles.css" for styling.
  • Links to external CSS files for fonts and icons.

4. <body>: Contains the content of the webpage.

  • <div class="wrap" data-pos="0">: Wraps the entire content of the webpage.
  • Various <div> elements represent different sections of the flight booking application such as headbar, header, content, form, list, ticket, etc.
  • Inside the sections are different HTML elements like <i>, <span>, <ul>, <input>, <button>, etc., used for displaying icons, text, lists, inputs, and buttons.
  • <script> tags include references to external JavaScript libraries and the application-specific script file "script.js".
  • External libraries include jQuery, nanoscroller, and underscore.js.

5. The content inside <script> tags typically contains JavaScript code for interactivity and functionality of the webpage.

This is the basic structure of our flight booking app using HTML, and now we can move on to styling it using CSS.

Step 2 (CSS Code):

Once the basic HTML structure of the app is in place, the next step is to add styling to the app using CSS.

Next, we will create our CSS file. In this file, we will use some basic CSS rules to style our app. Let's break it down section by section:

1. Variable Definitions:

  • Defines custom variables using the :root pseudo-class. These variables hold color values to be reused throughout the stylesheet.

2. Global Styling:

  • Sets minimum height for html and body.
  • Defines font family, text color, and background color for the entire page.

3. Styling for .wrap:

  • Defines styling for a container element with the class wrap. It sets background color, size, position, margins, and box shadow.

4. Styling for .headbar:

  • Defines styling for a header bar with the class headbar. It sets width, height, colors, alignment, padding, font size, and font weight.

5. Styling for .header:

  • Defines styling for a header section with the class header. It sets width, height, background image, opacity, and transition effects.

6. Styling for .content:

  • Defines styling for the content section with the class content. It sets height, background color, position, overflow, and transition effects.

7. Styling for .form, .list, .ticket:

  • Defines styling for form, list, and ticket sections. It sets float, width, height, padding, margin, color, and position.

8. Styling for .control:

  • Defines styling for control elements. It sets position, height, alignment, padding, background color, border, and transition effects.

9. Styling for .select and .dateinput:

  • Defines styling for select and date input elements. It sets opacity, transition, and calendar display properties.

10. Styling for .info-message:

  • Defines styling for informational messages. It sets margins, padding, font weight, font style, font size, color, and letter spacing.

11. Styling for .radio:

  • Defines styling for radio input elements. It sets background color and transition effects.

12. Styling for .spinner:

  • Defines styling for spinner elements. It sets position, width, height, and background color.

13. Styling for .list article:

  • Defines styling for list article elements. It sets display, width, height, transition effects, background color, opacity, and transform.

14. Styling for .ticket section:

  • Defines styling for ticket section elements. It sets background color, height, margin, and border radius.

15. Styling for .loader:

  • Defines styling for loader elements using keyframe animations. It sets border radius, width, height, color, font size, position, text indent, and animation effects.

This will give our flight booking app an upgraded presentation. Create a CSS file with the name of styles.css and paste the given codes into your CSS file. Remember that you must create a file with the .css extension.

:root {
  --app-bg-color: #1C1D21;
  --app-dark-color: #31353D;
  --app-accent-color: #445878;
  --app-alt-color: #92CDCF;
  --app-light-color: #EEEFF7;
}

html, body { min-height: 100% }

body {
  font-family: 'Roboto', sans-serif;
  color: #444;
  background-color: #EEEFF7;
  background-image:linear-gradient(120deg, #00c6ff 0%, #0072ff 100%);
}

.wrap {
  background-color: #1C1D21;
  position: relative;
  display: block;
  height: 610px;
  width: 365px;
  margin: 10px auto;
  box-shadow: 0px 2px 15px rgba(0, 0, 0, 0.3);
}

.headbar {
  width: calc(100% - 15px);
    height: 60px;
    color: #fff;
    background-color: var(--app-accent-color);
    display: flex;
    align-items: center;
    padding-left: 15px;
    font-size: 1.1em;
    font-weight: 500;
}

.headbar .btnBack { 
  margin-right: 15px;
  cursor: pointer; 
}

.header {
  width: 365px;
  height: 150px; 
  position: relative;
  background-image: linear-gradient(45deg, #445878 0%, #92CDCF 100%);
  transition: all 0.8s ease;
}

.header .bg,
.header .filter {
  position: absolute; 
  width: 100%; 
  height: 100%;
  background-color: rgba(112, 225, 245, 0.25);
}

.header .bg {
  background-image: url(https://dl.dropbox.com/s/8bg4zheauoyudeo/bg-city.jpg);
  background-size: 380px;
  opacity: 0;
  transition: all 0.8s ease;
}

.header .map {
  width: 100%;
  height: 100%;
  background-image: url(https://dl.dropbox.com/s/kvg6ykrz66kn9oe/map2.png);
  background-position: -110px -150px;
  /*background-position: -120px -120px;
  background-size: 680px;*/
  opacity: 1;
  transition: all .4s ease;
  position: absolute;
}

.header .title {
  display: none;
  align-items: center;
    justify-content: space-around;
    font-size: 2em;
    color: #fff;
    height: 100%;
}

.title > div { z-index: 10; }
.title span { 
  display: inline-block;
    font-weight: 400;
    font-size: 1.2em;
    text-shadow: 1px 1px 2px rgba(0,0,0,.25); 
}

div.rotate span { animation: rotate .6s linear }
div.rotate span:nth-child(2) { animation-delay: .1s }
div.rotate span:nth-child(3) { animation-delay: .3s }

@keyframes rotate {
  from { transform: rotateY(0deg); }
  to { transform: rotateY(360deg); }
}

.title .separator i {
  transform: rotate(90deg);
  font-size: .7em;
}

.content {
  height: calc(100% - 210px);
  background-color: var(--app-bg-color);
  position: relative;
  overflow: hidden;
  transition: all 0.8s ease;
}

.content > section {
  position: relative;
  width: 300%;
  height: 100%;
}

.wrap[data-pos="0"] .content > section { transform: translateX(0) }
.wrap[data-pos="1"] .content > section { transform: translateX(-365px) }
.wrap[data-pos="2"] .content > section { transform: translateX(-730px) }

.content > section > div { opacity: 0; }
.wrap[data-pos="0"] .content > section > div:nth-child(1) { opacity: 1; transition: opacity .8s ease; }
.wrap[data-pos="1"] .content > section > div:nth-child(2) { opacity: 1; transition: opacity .8s ease; }
.wrap[data-pos="2"] .content > section > div:nth-child(3) { opacity: 1; transition: opacity .8s ease; }

.wrap[data-pos="0"] .btnBack { display: none; }
.wrap[data-pos="0"] .header .title { display: flex }
.wrap[data-pos="0"] .header .bg { opacity: 1 }
.wrap[data-pos="0"] .header .map { opacity: 0 }

.wrap[data-pos="0"] .header { height: 140px }

.wrap[data-pos="0"] .content {
  height: calc(100% - 200px);
  transition: all 0.8s ease;
}

.wrap[data-pos="1"] .content .list article .img,
.wrap[data-pos="1"] .content .list article .info { 
  opacity: 1;
  transform: translateX(0);
}

.wrap[data-pos="2"] .header { height: 0px }
.wrap[data-pos="2"] .content { height: calc(100% - 80px); }

.form, .list, .ticket {
  float: left;
  width: 33.33333%;
  height: 100%;
  padding: 0;
  margin: 0;
  color: #e5e5e5;
  position: relative;
}

.control {
  position: relative;
  top: 0;
  height: 60px;
  display: flex;
  align-items: center;
  padding: 5px;
  background: #1C1D21; /*var(--app-bg-color);*/
  border-bottom: solid 1px rgba(255, 255, 255, 0.05);
  /*border-top: solid 1px rgba(0, 0, 0, 0.4);*/
  transition: all .4s ease;
}

.control:last-child {
  height: 55px;
  border-bottom: none;
  padding: 0;
}

/*Preview hack*/
.control-head > * { float: left; }

.control-head > i,
.control > i {
  font-size: 1.5em;
  margin-left: 15px;
}

.control-head > div,
.control > .control-item {
  margin-left: 20px;
}

.control h6 { 
  margin: 5px 0;
  font-weight: 400;
  color: #bbb;
}

.control.open:nth-child(2) { top: -72px; }
.control.open:nth-child(3) { top: -144px; }

.control.open {
  height: 100%;
  transition: all .4s ease;
}

.control .control-head { 
  display: flex; 
  align-items: center;
  margin-top: 5px; 
  cursor: pointer;
}

.control .control-body { 
  height: calc(100% - 72px);
  margin-top: 20px;
}

.control.open .control-body { 
  margin-top: 8px; 
}

.control.dateinput,
.control.select { display: block; }

.control .close {
  display: none;
  position: absolute;
  right: 15px;
  top: 15px;
  font-size: 20px;
}
.control.open .close {
  display: block;
  cursor: pointer;
}

/*** Begin Select Input ***/
.select .nano { 
  width: 304px;
  height: 418px;
  margin-left: 50px;
}

.select ul.select-index,
.select ul.select-data {
  margin: 0;
  margin-top: 10px;
  padding: 0;
  list-style: none;
}

.select.open ul.select-data li {
  opacity: 1;
  transform: translateY(0);
  transition: transform .6s ease;
}
.select ul.select-data li {
  margin: 5px 0;
  padding: 3px 15px;
  font-size: .85em;
  opacity: 0;
  transform: translateY(700px);
  transition: all .3s ease;
  cursor: pointer;
}

.select ul.select-data li:hover,
.select ul.select-data li.selected {
  background-color: rgba(0,0,0, .4);
}

.select ul.select-data li.sep {
  background-color: var(--app-accent-color);
}

.select ul.select-index {  
  position: absolute;
  left: 15px;
}

.select ul.select-index li {
  margin: 8px 0;
  padding: 2px 6px;
  border-radius: 50%;
  text-align: center;
  font-size: .9em;
  background-color: var(--app-accent-color);
  cursor: pointer;
}

/*** Begin Date Input ***/
.dateinput .control-body {
  opacity: 0;
  transition: opacity .6s ease;
}

.dateinput.open .control-body {
  opacity: 1;
  transition: opacity .8s ease;
}

.dateinput .calendar {
  margin-top: 10px;
}

.dateinput .calendar .month,
.dateinput .calendar .week,
.dateinput .calendar .days { 
  display: flex; 
  width: 100%;
  padding: 5px; 
}

.dateinput .calendar .month { 
  justify-content: space-around;
  background-color: var(--app-accent-color);
  margin-left: -5px; 
}

.dateinput .calendar .week,
.dateinput .calendar .days {
  flex-wrap: wrap;
}

.dateinput .calendar .week span,
.dateinput .calendar .days span {
  width: 40px;
  padding: 5px;
  text-align: center;
  font-weight: 300;
  font-size: .85em;
  position: relative;
}

.dateinput .calendar .week span {
  color: var(--app-alt-color);
  font-weight: 400;
}

.dateinput .calendar .days span:before {
  content: "";
  display: block;
  width: 22px;
  height: 22px;
  position: absolute;
  left: 14px;
  top: 3px;
  border-radius: 1px;
  background-color: transparent;
  transition: all .4s ease;
  cursor: pointer;
}

.dateinput .calendar .days span.checked:before {
  background-color: rgba(112, 225, 245, 0.3);
  border-radius: 50%;
  transition: all .4s ease;
}

.dateinput .calendar .days span:first-child {
  color: transparent;
}

.info-message { 
  margin-top: 30px;
    padding: 0 5px 0 15px;
    font-weight: 400;
    font-style: italic;
  font-size: .75em;
  color: var(--app-alt-color);
  letter-spacing: .06em; 
}
.info-message  i {
  margin-right: 5px;
    font-size: 1.2em;
}


/*** Begin Radio Input ***/
.radio label input[type="radio"] {  
  display: none;
}

.radio label > span,
.radio label > div {
  cursor: pointer;
  margin-right: 4px;
  padding: 4px 8px;
    border-radius: 3px;
    background-color: transparent;
    transition: background .4s ease;
}

.radio label input[type="radio"]:checked ~ span,
.radio label input[type="radio"]:checked ~ div {
  background-color: rgba(0, 0, 0, 0.4);
  transition: background .4s ease;
}

.radio label > div { display: inline-block; text-align: center; margin-right: 30px; }
.radio label > div small { display: block; font-size: .75em; }
.radio label > div span { margin-right: 4px; }
.radio label > div i { margin-left: 4px; }

/*** Begin Spinner Input ***/
.spinner {
  height: 100%;
  position: absolute;
  right: 0;
}
.spinner button {
  width: 42px;
  height: 50%;
  display: block;
  padding: 5px;
  border: none;
  color: #fff;
  background-color: var(--app-dark-color); 
  outline: none;
}

.spinner button:first-child {
  border-bottom: solid 1px #444;
}

.control > button {
  width: 100%;
  height: 100%;
  padding: 10px 2px;
  border: none;
  border-radius: 2px;
  color: #fff;
  background-color: var(--app-dark-color); /*#2196F3*/
  outline: none;
  text-transform: uppercase;
}

.control button:hover { background-color: rgba(255,255,255, .2); }

.list article {
  display: flex;
    width: 100%;
    height: 100px;
    transition: all 0.4s ease;
}

.list article:nth-child(2n+1) {
  background-color: rgba(255,255,255,.04);
}

.list article div.img {
  height: 100%;
  background-color: rgba(255, 255, 255, 0.05);
    display: flex;
    align-items: center;
    padding: 0 10px;
  opacity: 0;
  transform: translateX(-1000px);
  transition: all 0.6s cubic-bezier(0.645, 0.045, 0.355, 1);
}

.list article img {
  width: 48px;
    height: 48px;
    
}

.list article .info {
  padding: 10px;
  width: 100%;
  color: #e5e5e5;
  font-size: .8em;
  position: relative;
  opacity: 0;
  transform: translateX(1000px);
  transition: all 0.6s cubic-bezier(0.645, 0.045, 0.355, 1);
}

.list article:nth-child(2) > div { transition-delay: .12s }
.list article:nth-child(3) > div { transition-delay: .24s }
.list article:nth-child(4) > div { transition-delay: .36s }
.list article:nth-child(5) > div { transition-delay: .48s }


.info span { display: block; }
.info b { font-weight: 400; }
.info .time {
  color: #bbb;
  position: absolute;
  right: 10px;
  top: 10px;
}
.info .airline {
  display: block;
  font-size: 1.4em;
}

.info h5 {
  margin: 0;
  font-size: 1.4em;
      font-weight: 500;
    position: absolute;
    right: 10px;
    bottom: 10px;
}

.info h5 small {
  color: #bbb;
  font-size: .6em;
}

.ticket {
  display: flex;
  flex-direction: column;
    justify-content: center;
}

.ticket section {
  background-color: var(--app-dark-color);
  height: 350px;
  margin: 10px 20px;
    border-radius: 2px;
}

.ticket .title {
  display: flex;
    justify-content: space-around;
    margin: 15px 10px;
}

.ticket .title > div { text-align: center; }

.ticket .title span {
  font-size: 2em;
  text-shadow: none;
  display: block;
}

.ticket .title small { display: block; }

.ticket .separator i { position: relative; top: 10px; }

.ticket .row { 
  display: flex;
  justify-content: space-around;
  width: calc(100% + 40px);
  margin-top: 20px; 
}

.ticket .cell { 
      width: 140px;
}

.ticket .cell small {
  display: block;
  color: var(--app-alt-color);
}

.ticket .total {
  margin: 25px 40px 0;
    text-align: right;
}

.ticket .total span {
  font-size: 1.7em;
}

.ticket .total small {
  margin-right: 10px;
    color: #92cdcf;
}

.ticket button {
  height: 40px;
  padding: 10px 2px;
  margin: 10px 20px;
  border: none;
  border-radius: 2px;
  background-color: var(--app-accent-color);
  outline: none;
  color: var(--app-light-color);
  text-transform: uppercase;
  cursor: pointer;
}

.ticket button i {
  font-size: 16px;
    margin-right: 5px;
}

.btnHome { background-color: #432; }

/** Loader **/
.loader,
.loader:before,
.loader:after {
  border-radius: 50%;
  width: 1.8em;
  height: 1.8em;
  -webkit-animation-fill-mode: both;
  animation-fill-mode: both;
  -webkit-animation: load7 1.8s infinite ease-in-out;
  animation: load7 1.8s infinite ease-in-out;
}
.loader {
  display: none;
  color: #ffffff;
  font-size: 10px;
  margin: 5px auto;
  position: relative;
  text-indent: -9999em;
  -webkit-transform: translateZ(0);
  -ms-transform: translateZ(0);
  transform: translateZ(0);
  -webkit-animation-delay: -0.16s;
  animation-delay: -0.16s;
}
.loader:before,
.loader:after {
  content: '';
  position: absolute;
  top: 0;
}
.loader:before {
  left: -3.5em;
  -webkit-animation-delay: -0.32s;
  animation-delay: -0.32s;
}
.loader:after {
  left: 3.5em;
}
@-webkit-keyframes load7 {
  0%,
  80%,
  100% {
    box-shadow: 0 2.5em 0 -1.3em;
  }
  40% {
    box-shadow: 0 2.5em 0 0;
  }
}
@keyframes load7 {
  0%,
  80%,
  100% {
    box-shadow: 0 2.5em 0 -1.3em;
  }
  40% {
    box-shadow: 0 2.5em 0 0;
  }
} 

Step 3 (JavaScript Code):

Finally, we need to create a function in JavaScript.

Let's break it down step by step:

1. Data Setup: The code initializes three arrays: flights, carrier, and airports.

  • flights contain information about different flights, including currency, price, carrier, time, and nodes.
  • carrier maps carrier codes to their corresponding full names.
  • airports contain information about various airports, including their names, cities, countries, and IATA codes.

2. Function Invocation: The code is wrapped within an immediately-invoked function expression (IIFE), which ensures that the code inside it executes immediately when the script is loaded.

3. Data Manipulation and Rendering:

  • The code organizes the airports by country and generates HTML elements for selecting airports, including country separators.
  • It populates a calendar with days and marks one of them as checked.
  • It calls the doFlightsRender function to render flight results.

4. Event Handling:

  • It handles various events such as clicking on the control head, closing controls, changing passenger counts, selecting airports, selecting dates, and clicking search or back buttons.
  • It also handles selecting a flight from the list.

5. Flight Rendering Function: The doFlightsRender function is responsible for rendering flight information. It takes into account the selected departure and arrival airports, the selected date, and the number of passengers. It calculates the total price based on the selected flight and passenger count.

6. Animation: The code includes animations for rotating elements when selecting departure and arrival airports.

7. Flight Booking Simulation: It simulates a flight booking process by showing a loader and then marking the flight as booked after a delay.

8. Scrolling Functionality: The code initializes scrolling functionality using the NanoScroller library for elements with the class nano.

Create a JavaScript file with the name script.js and paste the given codes into your JavaScript file and make sure it's linked properly to your HTML document so that the scripts are executed on the page. Remember, you’ve to create a file with .js extension.

// Play with the inputs -->
var flights = [
  {
    currency: "EUR",
    price: 128.67,
    carrier: "KL",
    time: "2h 30min",
    nodes: ["CDG 2017-05-30T09:35+02:00 AMS 2017-05-30T11:15+02:00"] },
  
  {
    currency: "EUR",
    price: 138.70,
    carrier: "AF",
    time: "2h 30min",
    nodes: ["CDG 2017-05-30T12:35+02:00 AMS 2017-05-30T13:50+02:00"] },
  
  {
    currency: "EUR",
    price: 151.41,
    carrier: "BA",
    time: "2h 30min",
    nodes: ["CDG 2017-05-30T11:40+02:00 AMS 2017-05-30T12:55+02:00"] },
  
  {
    currency: "EUR",
    price: 174.70,
    carrier: "KL",
    time: "2h 30min",
    nodes: ["CDG 2017-05-30T18:35+02:00 AMS 2017-05-30T19:50+02:00"] },
  
  {
    currency: "EUR",
    price: 204.70,
    carrier: "AF",
    time: "2h 30min",
    nodes: ["CDG 2017-05-30T11:40+02:00 AMS 2017-05-30T12:55+02:00"] }];
  
  
  
  var carrier = {
    "AF": "Air France",
    "KL": "KLM Royal Dutch Airlines",
    "BA": "British Airways" };
  
  
  var airports = [{ "name": "Vichy-Charmeil Airport", "city": "Vichy", "country": "France", "IATA": "VHY" }, { "name": "Lyon-Bron Airport", "city": "Lyon", "country": "France", "IATA": "LYN" }, { "name": "Cannes-Mandelieu Airport", "city": "Cannes", "country": "France", "IATA": "CEQ" }, { "name": "Marseille Provence Airport", "city": "Marseille", "country": "France", "IATA": "MRS" }, { "name": "Charles de Gaulle International", "city": "Paris", "country": "France", "IATA": "CDG" }, { "name": "Toussus-le-Noble Airport", "city": "Toussous-le-noble", "country": "France", "IATA": "TNF" }, { "name": "Paris-Orly Airport", "city": "Paris", "country": "France", "IATA": "ORY" }, { "name": "Le Mans-Arnage Airport", "city": "Le Mans", "country": "France", "IATA": "LME" }, { "name": "Nantes Atlantique Airport", "city": "Nantes", "country": "France", "IATA": "NTE" }, { "name": "Nancy-Essey Airport", "city": "Nancy", "country": "France", "IATA": "ENC" }, { "name": "Frankfurt am Main International", "city": "Frankfurt", "country": "Germany", "IATA": "FRA" }, { "name": "Hamburg Airport", "city": "Hamburg", "country": "Germany", "IATA": "HAM" }, { "name": "Cologne Bonn Airport", "city": "Cologne", "country": "Germany", "IATA": "CGN" }, { "name": "Munich International Airport", "city": "Munich", "country": "Germany", "IATA": "MUC" }, { "name": "Stuttgart Airport", "city": "Stuttgart", "country": "Germany", "IATA": "STR" }, { "name": "Berlin-Tegel International Airport", "city": "Berlin", "country": "Germany", "IATA": "TXL" }, { "name": "Hannover Airport", "city": "Hannover", "country": "Germany", "IATA": "HAJ" }, { "name": "Bremen Airport", "city": "Bremen", "country": "Germany", "IATA": "BRE" }, { "name": "Frankfurt-Hahn Airport", "city": "Hahn", "country": "Germany", "IATA": "HHN" }, { "name": "Dortmund Airport", "city": "Dortmund", "country": "Germany", "IATA": "DTM" }, { "name": "Cork Airport", "city": "Cork", "country": "Ireland", "IATA": "ORK" }, { "name": "Galway Airport", "city": "Galway", "country": "Ireland", "IATA": "GWY" }, { "name": "Dublin Airport", "city": "Dublin", "country": "Ireland", "IATA": "DUB" }, { "name": "Waterford Airport", "city": "Waterford", "country": "Ireland", "IATA": "WAT" }, { "name": "Amsterdam Airport Schiphol", "city": "Amsterdam", "country": "Netherlands", "IATA": "AMS" }, { "name": "Maastricht Aachen Airport", "city": "Maastricht", "country": "Netherlands", "IATA": "MST" }, { "name": "Eindhoven Airport", "city": "Eindhoven", "country": "Netherlands", "IATA": "EIN" }, { "name": "Rotterdam The Hague Airport", "city": "Rotterdam", "country": "Netherlands", "IATA": "RTM" }, { "name": "Belfast International Airport", "city": "Belfast", "country": "United Kingdom", "IATA": "BFS" }, { "name": "Manchester Airport", "city": "Manchester", "country": "United Kingdom", "IATA": "MAN" }, { "name": "Southampton Airport", "city": "Southampton", "country": "United Kingdom", "IATA": "SOU" }, { "name": "London Heathrow Airport", "city": "London", "country": "United Kingdom", "IATA": "LHR" }, { "name": "Blackpool International Airport", "city": "Blackpool", "country": "United Kingdom", "IATA": "BLK" }, { "name": "Newcastle Airport", "city": "Newcastle", "country": "United Kingdom", "IATA": "NCL" }, { "name": "London Stansted Airport", "city": "London", "country": "United Kingdom", "IATA": "STN" }, { "name": "Miami International Airport", "city": "Miami", "country": "United States", "IATA": "MIA" }, { "name": "John F Kennedy International Airport", "city": "New York", "country": "United States", "IATA": "JFK" }, { "name": "Piedmont Triad International Airport", "city": "Greensboro", "country": "United States", "IATA": "GSO" }, { "name": "Wings Field", "city": "Philadelphia", "country": "United States", "IATA": "BBX" }, { "name": "Hardwick Field", "city": "Cleveland", "country": "United States", "IATA": "HDI" }, { "name": "Warren Field", "city": "Washington", "country": "United States", "IATA": "OCW" }];
  
  (function () {
  
    var _airports = _.groupBy(airports, o => o.country),
    selectIndex = [],
    selectData = [];
  
    _.each(_airports, (countryList, countryName) => {
      var firstLeter = countryName.split('')[0];
      selectData.push(`<li class="sep" data-index="${firstLeter}">${countryName}</li>`);
      selectIndex.push(`<li>${firstLeter}</li>`);
  
      _.each(countryList, (airport, i) => {
        selectData.push(`<li data-iata="${airport.IATA}" data-city="${airport.city}">
          ${airport.IATA}, ${airport.name}</li>`);
      });
    });
  
    $('.select ul.select-index').html(_.uniq(selectIndex).join(''));
    $('.select ul.select-data').html(selectData.join(''));
  
  
    // Calendar days
    var days = [30];
    for (var i = 0; i < 31; i++) {days.push(i);}
  
    var daysRender = _.map(days, function (i) {
      return `<span>${i + 1}</span>`;
    });
  
    $('.calendar .days').html(daysRender.join(''));
    $('.calendar .days span').eq(8).addClass('checked');
  
    // Flight Results
    doFlightsRender(true);
  
  
    // Events
    // Open inputs
    $('.control:not(.open) .control-head').on('click', function (evt) {
      $(evt.currentTarget).parent('.control').addClass('open');
    });
  
    $('.control .close').on('click', function (evt) {
      var z = $(evt.currentTarget).closest('.control');
      setTimeout(() => {z.removeClass('open');}, 50);
    });
  
    // SpinnerInput add/substract action
    $('.spinner button').on('click', function (evt) {
      var isAdding = evt.currentTarget.getAttribute('data-action') == 'plus',
      $input = $('input[name="passengers"]:checked'),
      $control = $input.siblings('div').find('span'),
      value = parseInt($control.text());
  
      if (isAdding)
      value++;else
      if (value !== 0)
      value--;
  
      $control.text(value);
    });
  
    // SelectInput find index
    $('.select-index').on('click', 'li', function (evt) {
      var index = evt.currentTarget.textContent,
      $nano = $(evt.currentTarget).parent('.select-index').siblings('.nano'),
      el = $nano.find(`li.sep[data-index="${index}"]`)[0];
  
      $nano.find('.nano-content').animate({ scrollTop: el.offsetTop }, 600);
    });
  
    // SelectInput set data
    $('.select-data').on('click', 'li:not(.sep)', function (evt) {
      var text = evt.currentTarget.textContent,
      iata = evt.currentTarget.getAttribute('data-iata'),
      $select = $(evt.currentTarget).closest('.select'),
      $nameContainer = $select.find('.airport-name');
  
      if ($nameContainer.data('role') == 'from') {
        var _iata = iata.split('');
        var div = $('.header .fromPlace').addClass('rotate');
        var span = $('.header .fromPlace span');
        span.eq(0).text(_iata[0]);
        span.eq(1).text(_iata[1]);
        span.eq(2).text(_iata[2]);
        setTimeout(() => div.removeClass('rotate'), 900);
        //$('.xfrom').text(iata);
      } else
      {
        var _iata = iata.split('');
        var div = $('.header .toPlace').addClass('rotate');
        var span = $('.header .toPlace span');
        span.eq(0).text(_iata[0]);
        span.eq(1).text(_iata[1]);
        span.eq(2).text(_iata[2]);
        setTimeout(() => div.removeClass('rotate'), 900);
        //$('.xto').text(iata);
      }
  
      $nameContainer.text(text);
      $select.toggleClass('open');
  
      $(evt.currentTarget).addClass('selected').siblings('li').removeClass('selected');
    });
  
    // Date input
    $('.calendar .days span').on('click', function (evt) {
      var $this = $(evt.currentTarget),
      day = evt.currentTarget.textContent;
  
      $this.addClass('checked').siblings('span').removeClass('checked');
  
      var date = new Date(`5/${day}/2017`);
      var [wd, m, d] = date.toDateString().split(' ');
      $('.dateinput .control-item span').eq(0).text(`${wd.toUpperCase()}, ${d} ${m}`);
    });
  
  
    $('.btnBack').on('click', function (evt) {
      var wrap = document.querySelector('.wrap'),
      index = parseInt(wrap.getAttribute('data-pos'));
  
      $('.ticket button.btnBook').text('Book Flight');
      wrap.setAttribute('data-pos', index - 1);
    });
  
    // Search button
    $('.btnSearch').on('click', function (evt) {
      doFlightsRender(false);
      setTimeout(() => {
        document.querySelector('.wrap').setAttribute('data-pos', 1);
      }, 50);
    });
  
    $('.ticket button').on('click', function (evt) {
      var $button = $(evt.currentTarget);
      var $loader = $('.loader').show();
      $button.text('Booking...');
  
      setTimeout(() => {
        $loader.hide();
        $button.html('<i class="zmdi zmdi-check-circle"></i> Flight Booked');
        $button.addClass('success');
      }, 1200);
    });
  
    // Select Flight
    $('.list').on('click', 'article', function (evt) {
      var index = parseInt(evt.currentTarget.getAttribute('data-index')),
      flight = flights[index];
  
      var [from, t1, to, t2] = flight.nodes[0].split(' ');
  
      var p = $('.radio.passengers label span'),
      peopleTotal = 0,
      people = _.map(p, function (el, i) {
        var v = parseInt(el.textContent),
        str = '';
  
        if (i == 0 && v)
        str = `${v} Adults`;
        if (i == 1 && v)
        str = `${v} Kids`;
        if (i == 2 && v)
        str = `${v} Elders`;
  
        peopleTotal += v;
  
        return str;
      });
  
      from = $('.fromPlace span').text();
      to = $('.toPlace span').text();
  
      var time1 = new Date(t1),
      time2 = new Date(t2);
  
      var clase = $('input[name="seat"]:checked').val(),
      dates = $('.dateinput .control-item span'),
      place1 = _.findWhere(airports, { IATA: from }),
      place2 = _.findWhere(airports, { IATA: to });
  
      var flightRender = `
        <div class="title">
          <div>
            <small>${time1.toLocaleTimeString().replace(':00', '')}</small>
            <span>${from}</span>
            <small>${place1.city}</small>
          </div>
          <span class="separator"><i class="zmdi zmdi-airplane"></i></span>
          <div>
            <small>${time2.toLocaleTimeString().replace(':00', '')}</small>
            <span>${to}</span>
            <small>${place2.city}</small>
          </div>
        </div>
        <div class="row">
          <div class="cell">
            <small>Passengers</small><span>${_.compact(people).join(',')}</span>
          </div>
          <div class="cell">
            <small>Class</small><span>${clase}</span>
          </div>
        </div>
        <div class="row">
          <div class="cell">
            <small>Departure</small><span>${dates[0].textContent}</span>
          </div>
          <div class="cell">
            <small>Return</small><span>${dates[1].textContent}</span>
          </div>
        </div>
        <div class="row">
          <div class="cell">
            <small>Airline</small><span>${carrier[flight.carrier]}</span>
          </div>
          <div class="cell">
          </div>
        </div>
        <div class="total">
          <small>Total</small> <span>€${(flight.price * peopleTotal).toFixed(2)}</span>
        </div>
      `;
  
      $('.ticket section').html(flightRender);
      setTimeout(() => {
        document.querySelector('.wrap').setAttribute('data-pos', 2);
      }, 50);
    });
  
    // Init scroll
    $(".nano").nanoScroller();
  
  
    function doFlightsRender(isInit) {
      var flightsRender = _.map(flights, function (o, i) {
        var sumText = "";
        var [from, t1, to, t2] = o.nodes[0].split(' ');
  
        var d1 = new Date(t1),
        d2 = new Date(t2);
  
        if (!isInit) {
          var ppl = $('.radio.passengers label span'),
          sum = parseInt(ppl.eq(0).text()) + parseInt(ppl.eq(1).text()) + parseInt(ppl.eq(2).text());
  
          sumText = `${sum} people €${(o.price * sum).toFixed(2)}`;
          from = $('.fromPlace span').text();
          to = $('.toPlace span').text();
        }
  
        var img;
        if (o.carrier == 'KL')
        img = 'https://dl.dropbox.com/s/02ve5kn75rpo0s3/KL.png';else
        if (o.carrier == 'BA')
        img = 'https://dl.dropbox.com/s/6fpuy898zzuk7nn/BA.png';else
  
        img = 'https://dl.dropbox.com/s/dhmufay65yer2jz/AF.png';
  
        return `<article data-index="${i}">
          <div class="img">
            <img src="${img}" alt="ualogo" />
          </div>
          <div class="info">
            <span class="time">${o.time}</span>
            <span class="airline">
              ${d1.toLocaleTimeString().replace(':00', '')} - 
              ${d2.toLocaleTimeString().replace(':00', '')}
            </span>
            <span>${carrier[o.carrier]} ${from} - ${to}</span>
            <span>Non-Stop</span>
  
            <h5><small>${sumText}</small> €${o.price}</h5>
          </div>
        </article>`;
      });
  
      $('.list .nano-content').html(flightsRender.join(''));
    }
  
  })();

Final Output:

Develop a Flight Booking App Using HTML, CSS, and JavaScript.gif

See the Pen Flight Booking App by Eduardo Moreno (@emoreno911) on CodePen.

Conclusion:

Congratulations! You've reached the end of our comprehensive guide on creating a flight booking app using HTML, CSS, and JavaScript. Throughout this tutorial, we've covered everything you need to know to embark on this exciting journey of front-end development.

Remember, the journey of app development is not without its challenges. Embrace each obstacle as an opportunity to learn and grow. Don't hesitate to explore additional resources, seek help from the developer community, and experiment with new techniques and technologies.

Whether you're building a flight booking app for personal use, as a portfolio project, or to meet the needs of a specific audience, your dedication and passion for coding will undoubtedly shine through in your creations.

So, what are you waiting for? Start coding, unleash your creativity, and embark on your next adventure in web development. Happy coding!

Design and Code by: Eduardo Moreno

That’s a wrap!

I hope you enjoyed this post. Now, with these examples, you can create your own amazing page.

Did you like it? Let me know in the comments below 🔥 and you can support me by buying me a coffee.

And don’t forget to sign up to our email newsletter so you can get useful content like this sent right to your inbox!

Thanks!
Faraz 😊

End of the article

Subscribe to my Newsletter

Get the latest posts delivered right to your inbox


Latest Post