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;
}



Love this post?


Help me to buy a cup of coffee/Support us by donating.
All the donations will be going to the website maintenance/improvement.

Pay Now

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 *