Building Recepie App using HTML + CSS + Javascript + jQuery + Bootstrap 4

Hello guys, Bushan here welcomes back to B2 Tech. Today in this post, we will be creating a Recipe App using HTML, CSS, Javascript, jQuery, and Bootstrap. The fully working application is already deployed to the live web server, you can find the app here. The application uses browsers local storage to store the data. Alright, let’s jump into coding.


Application Features

  • Add Recipe
  • Update Recipe
  • Delete Recipe
  • Display Recipes
  • Add ingredients
  • Remove ingredients

Tools and Technologies




index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="css/bootstrap.min.css">
    <link rel="stylesheet" href="css/style.css">
    <title>Recepie App</title>
</head>
<body>
    
    <div class="wrapper">
        <div class="content">
            <div class="header">
                <div class="container">
                    <h1>Recepie App</h1>
                    <p>Add Recepies that never before</p>
                </div>
            </div>
        
            <div class="actions">
                <div class="container">
                    <div class="row">
                        <div class="col-sm-4 col-8">
                            <input type="text" id="filter-recepies" placeholder="Filter Recepies" class="form-control"/>
                        </div>
                        <div class="col-sm-8 col-4">
                            <button id="add-recepie" class="btn btn-primary" style="float: right;">Add Recepie</button>
                        </div>
                    </div>
                </div>
            </div>
        
            <div class="container">
                <div class="recepies"></div>
            </div>
        </div>
        <div class="footer">
            <div class="container">
                <div class="row">
                    <div class="col-sm-12 col-10">
                        <p>Designed and Developed by Bushan Sirgur (c) <a href="https://bushansirgur.in">B2 Tech</a></p>
                    </div>
                </div>
            </div>
        </div>
        <script src="scripts/uuidv4.js"></script>
        <script src="scripts/jquery.js"></script>
        <script src="scripts/bootstrap.min.js"></script>
        <script src="scripts/recepie-app.js"></script>
    </div>
</body>
</html>

edit.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=320, height=device-height, target-densitydpi=medium-dpi" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="css/bootstrap.min.css">
    <link rel="stylesheet" href="css/style.css">
    <title>Edit Recepie</title>
</head>
<body>

    <div class="wrapper">
        <div class="content">
            <div class="header">
                <div class="container">
                    <h1>Recepie App</h1>
                    <p>Add Recepies that never before</p>
                </div>
            </div>
            
            <div class="edit-actions">
                <div class="container">
                    <a href="/index.html">Home</a>
                </div>
            </div>
            
            <div class="main-content">
                <div class="container">
                    <h2>Add Recepie</h2>
                    <div class="form-group">
                        <input type="text" placeholder="Recepie Name" id="recepie-name" class="form-control"/>
                    </div>
                    
                    <div class="form-group">
                        <textarea placeholder="Description" id="recepie-steps" class="form-control"></textarea>
                    </div>
                    
                    <h2>Ingredients</h2>
                    <div class="ingredients"></div>
                
                    <div class="row">
                        <div class="col-sm-3 col-7">
                            <input type="text" id="add-ingredient" placeholder="Add Ingredient" class="form-control"/>
                        </div>
                        <div class="col-sm-3 col-5">
                            <button id="btn-ingredient" class="btn btn-primary">Add Ingredient</button>
                        </div>
                    </div>
        
                    <br/>
                    <button onclick="window.location.href='/index.html'" class="btn btn-primary">Save Recepie</button>
                    <button id="delete-recepie" class="btn btn-danger">Delete Recepie</button>
                </div>
            </div>
        </div>
        <div class="footer">
            <div class="container">
                <div class="row">
                    <div class="col-sm-12 col-10">
                        <p>Designed and Developed by Bushan Sirgur (c) <a href="https://bushansirgur.in">B2 Tech</a></p>
                    </div>
                </div>
            </div>
        </div>

        <script src="scripts/jquery.js"></script>
        <script src="scripts/bootstrap.min.js"></script>
        <script src="scripts/uuidv4.js"></script>
        <script src="scripts/recepie-app.js"></script>
        <script src="scripts/recepie-edit.js"></script>    
    </div>
</body>
</html>




recepie-app.js

let recepies = getSavedRecepies();

const filters = {
    searchText: ''
}

function getSummaryInfo(element) {
    const summaryEl = $('<p>');
    let falses=0,trues=0;
    $.each(element.ingredients, function name(index, element) {
        if (element.flag) {
            trues++;
        }else{
            falses++;
        }
    })

    if (element.ingredients.length == trues) {
        summaryEl.text('You have all ingredients');
    }else if(element.ingredients.length == falses){
        summaryEl.text('You have none of ingredients');
    }else{
        summaryEl.text('You have some of ingredients');
    }
    return summaryEl;
}

function renderRecepies(recepies, filters) {
    let filteredRecepies = $.grep(recepies, function (element) {
        return element.name.toLowerCase().includes(filters.searchText.toLowerCase());
    })
    $('.recepies').empty();
    $.each(filteredRecepies, function (index, element) {
        let summaryEl = getSummaryInfo(element);
        summaryEl.addClass('list-item__subtitle');
        let cardEl = $('<a>');
        cardEl.addClass('list-item');
        let titleEl = $('<p>');
        titleEl.addClass('list-item__title');
        if(element.name.length > 0){
            titleEl.text(element.name);
        }else{
            titleEl.text('Unnamed Recepie');
        }
        cardEl.attr('href', '/edit.html#'+element.id);
        cardEl.append(titleEl);
        cardEl.append(summaryEl);
        $('.recepies').append(cardEl);
    })
}

$('#filter-recepies').on('input', function (e) {
    filters.searchText = e.target.value;
    renderRecepies(recepies, filters);
})

$('#add-recepie').click(function () {
    const id = uuidv4();
    recepies.push({
        id: id,
        name: '',
        description: '',
        ingredients: []
    })
    saveRecepies(recepies);
    location.assign('/edit.html#'+id);
})

function getSavedRecepies() {
    const recepiesJSON = localStorage.getItem('recepies');
    if (recepiesJSON !== null) {
        return JSON.parse(recepiesJSON);    
    }else{
        return [];
    }
    
}

function saveRecepies(recepies) {
    localStorage.setItem('recepies', JSON.stringify(recepies));
}

renderRecepies(recepies, filters);

recepie-edit.js

const recepieId = location.hash.substr(1);
let recepie = recepies.find(function (recepie) {
    return recepie.id === recepieId;
})

if (recepie === undefined) {
    location.assign('/index.html');
}

$('#recepie-name').val(recepie.name);
$('#recepie-steps').val(recepie.description);
renderIngredients(recepie);

$('#recepie-name').on('input', function (e) {
    recepie.name = e.target.value;
    saveRecepies(recepies);
})

$('#recepie-steps').on('input', function (e) {
    recepie.description = e.target.value;
    saveRecepies(recepies);
})

$('#delete-recepie').click(function () {
    const recepieIndex = recepies.findIndex(recepie => recepie.id === recepieId);
    if(recepieIndex != -1){
        recepies.splice(recepieIndex, 1);
    }
    saveRecepies(recepies);
    location.assign('/index.html');
})

$('#btn-ingredient').click(function () {
    let ingredient = '';
    let id = uuidv4();
    if ($('#add-ingredient').val().length > 0) {
        ingredient = $('#add-ingredient').val();    
    }else{
        ingredient = '';
    }
    
    recepie.ingredients.push({
        id: id,
        name: ingredient,
        flag: false
    });

    saveRecepies(recepies);
    renderIngredients(recepie);
    $('#add-ingredient').val('');
})

function renderIngredients(recepie) {
    $('.ingredients').empty();
    $.each(recepie.ingredients, function (index, element) {
        $('.ingredients').append(generateIngredientDOM(element));
    })
}

function toggleIngredient(element) {
    element.flag = !element.flag;
}

function generateIngredientDOM(element) {
    const rootEl = $('<div>');
    const checkboxEl = $('<input type="checkbox" />');
    const textEl = $('<span>');
    const buttonEl = $('<a>');
    const labelEl = $('<label>');
    const spanEl = $('<span>');
    
    spanEl.addClass('spanStyle');
    buttonEl.attr('href', '#');
    rootEl.addClass('form-check');
    rootEl.addClass('ingredient-card');
    rootEl.addClass('col-sm-4');
    checkboxEl.addClass('form-check-input');
    
    buttonEl.text('Remove');
    checkboxEl.attr('checked', element.flag);

    buttonEl.click(function () {
        removeIngredient(element.id);
        saveRecepies(recepies);
        renderIngredients(recepie);
    })

    checkboxEl.change(function () {
        toggleIngredient(element);
        saveRecepies(recepies);
        renderIngredients(recepie);
    })

    if(element.name.length > 0){
        textEl.text(element.name);
    }else{
        textEl.text('Unnamed ingredient');
    }
    
    labelEl.append(checkboxEl);
    labelEl.append(textEl);
    rootEl.append(labelEl);
    spanEl.append(buttonEl);
    rootEl.append(spanEl);

    return rootEl;
}

function removeIngredient(id) {
    const ingredientIndex = recepie.ingredients.findIndex(ingredient => ingredient.id === id);
    if(ingredientIndex != -1){
        recepie.ingredients.splice(ingredientIndex, 1);
    }
}




style.css

div.header{
    background-color: #EA7773;
    height: 100px;
    padding: 10px;
}

div.actions{
    background-color: #EAF0F1;
    height: 75px;
    padding: 20px;
}

div.main-content{
    margin-top: 10px;
}

.list-item{
    text-decoration: none;
    color: #333333;
    background: #F7F7F7;
    border: 1px solid #dedfe0;
    margin: 1.3rem 0;
    padding-left: 1.6rem;
    padding-top: 0.6rem;
    display: block;
    transition: background .3s ease;
}

.list-item:hover {
    background: #eeeeee;
    text-decoration: none;
    color: black;
}

.list-item__title {
    font-size: 1.8rem;
    margin-bottom: .4rem
}

.list-item__subtitle {
    color: #666;
    font-size: 1.2rem;
    font-weight: 300;
    font-style: italic;
}

.ingredient-card{
    border: 1px solid #dedfe0;
    margin: 1rem 0;
    background: #F7F7F7;
    color: #333333;
    padding-left: 1.5rem;
    padding-top: 0.5rem;
}

.ingredient-card:hover{
    background: #eeeeee;
    text-decoration: none;
    color: black;
}

.spanStyle{
    float: right;
}
  
@media (min-width: 320px) and (max-width: 480px) {
    #btn-ingredient{
        float: right;
    }
    #add-recepie{
        margin-right: -15px;
    }
    #filter-recepies{
        margin-left: -15px;
    }
}

.edit-actions{
    background-color: #EAF0F1;
    height: 50px;
    padding-top: 10px;
    font-size: 20px;
}

a:hover{
    text-decoration: none;
}

body, html{
    height: 100%;
}

.wrapper{
    min-height: 100%;
    position: relative;
}
.content{
    padding-bottom: 100px;
}

.footer{
    position: absolute;
    bottom: 10px;
    height: 60px;
}



That’s it for now if you have any queries regarding this post leave your comments in the comment section or you can reach out to me [email protected]
I will see you in the next post.



Thanks
-B2 Tech Team

About the author

Bushan Sirgur

Well, I am Bushan Sirgur from Banglore, India. Currently, I am working as a Software Developer in a Service Base Company. I am interested in JAVA/J2EE, Angular 2, JavaScript, jQuery, MongoDB.

View all posts

Leave a Reply

Your email address will not be published. Required fields are marked *