Bạn đang muốn giảm cân hoặc giữ dáng? Điều quan trọng nhất ngoài tập thể dục đó là kiểm soát lượng Calories nạp vào cơ thể. Để làm được điều đó, bạn cần tính chính xác số lượng Calories của thực phẩm ăn vào. Vậy chúng ta sẽ xây dựng một trang Tính Calo trên nền tảng giao diện Bootstrap và jQuery, tại sao không?

Ứng dụng Tính Calo đơn giản

Calories là đơn vị nhằm để xác định năng lượng chứa trong thực phẩm và đồ uống ta tiêu thụ hằng ngày. Calories được đốt cháy để chuyển hóa thành năng lượng phục vụ các nhu cầu hoạt động của cơ thể, nếu dung nạp vượt quá nhu cầu (khoảng 2000), nó sẽ được dự trữ và tích tụ trong cơ thể dưới dạng chất béo và chuyển hóa thành mỡ. 7.700 Calories tương ứng với một kg mỡ.

Vì vậy, chúng ta sẽ xây dựng công cụ Tính Calo để kiểm soát lượng Calories hàng ngày.  Để thực hành bài viết này, các bạn cần:

Giới thiệu

Công cụ này tính Calories từ danh sách thực phẩm mà người Việt hay dùng, tham khảo tại đây. Để tính cần chọn loại thực phẩm từ bảng bên dưới, nhập khối lượng sử dụng và bấm Thêm Vào Danh Sách.

Calo Calc

Bấm vào hình để xem kích thước lớn hơn.

Bắt đầu

Tạo thư mục calo-calc trong htdocs như sau:

calo-calc/
├── data/
│   ├── data.json
│   ├── type.json
├── img/
│   ├── favicon.ico
├── index.html

Trong đó, tập tin data.json có định dạng:

[
    {
        "type": "Cháo, phở, miến, mì ăn liền",
        "name": "Bún ăn liền",
        "calo": "348.0",
        "water": "22",
        "protein": "6.4",
        "fat": "9.0",
        "carb": "60.0",
        "df": "0.5"
    },
    {
        "type": "Cháo, phở, miến, mì ăn liền",
        "name": "Cháo ăn liền",
        "calo": "346.0",
        "water": "17",
        "protein": "6.8",
        "fat": "4.4",
        "carb": "70.0",
        "df": "0.5"
    },
    ...
]

Với mỗi {} là một thực phẩm. Tập tin type.json chứa các loại thực phẩm (dùng để làm chức năng phân loại).

[
    {
        "type": "Cháo, phở, miến, mì ăn liền"
    },
    {
        "type": "Củ giàu tinh bột"
    },
    ...
]

Đừng lo, mình đã có lấy dữ liệu sẵn và sẽ cung cấp 2 tập tin này, bạn có thể tại về tại đây.

HTML

Tại tập tin index.html:

<!doctype html>
<html class="no-js" lang="vi">
<head>
<meta name="robots" content="noindex,follow">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Calo-Calc v1.1 | Công Cụ Tính Calo</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="shortcut icon" href="img/favicon.ico">
<style type="text/css">
    body { padding-top: 50px; }
    .cursor-pointer { cursor:pointer; }
    .m-b-1 { margin-bottom: 1em; }
    .label { font-size: 0.8em; }
</style>
</head>
<body>
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button>
            <a class="navbar-brand" href="./">Calo-Calc v1.1</a> </div>
        <div id="navbar" class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
            	<li><a href="#nutri-calc">Tính Calo</a></li>
                <li><a href="#nutri-table">Bảng Thành Phần Dinh Dưỡng</a></li>
            </ul>
        </div>
        <!-- /navbar-collapse --> 
    </div>
</nav>

<div class="container"> 
    <div class="row">
        <div class="col-lg-12">
            <div id="nutri-calc" class="page-header">
                <h3>Tính Thành Phần Dinh Dưỡng</h3>
            </div>
            <div>
                <form class="form-inline">
                    <div id="food-input" class="form-group">
                        <label>Tên Thực Phẩm</label>
                        <input id="food-name" data-id="false" type="text" class="form-control" placeholder="Chọn bên dưới..." disabled>
                    </div>
                    <div id="weight-input" class="form-group">
                        <label>Khối Lượng (g)</label>
                        <input id="food-weight" type="number" class="form-control" placeholder="Nhập số..." value="100" step="100" required>
                    </div>
                    <button id="food-submit" type="button" class="btn btn-primary">Thêm Vào Danh Sách</button>
                </form>
                <hr>
                <table class="table table-striped">
                    <thead>
                        <tr>
                            <th>#</th>
                            <th>Tên Thực Phẩm</th>
                            <th>Khối Lượng (g)</th>
                            <th>Năng Lượng (kcal)</th>
                            <th class="hidden-xs">Nước (g)</th>
                            <th class="hidden-xs">Đạm (g)</th>
                            <th class="hidden-xs">Tinh Bột (g)</th>
                            <th class="hidden-xs">Béo (g)</th>
                            <th class="hidden-xs">Xơ (g)</th>
                        </tr>
                    </thead>
                    <tbody id="nutri-table-select"></tbody>
                    <tbody>
                        <tr class="info">
                            <th colspan="2">Tổng Cộng:</th>
                            <td id="we-val">?</td>
                            <td id="ca-val"><span class="text-danger">?</span></td>
                            <td id="wa-val" class="hidden-xs">?</td>
                            <td id="pro-val" class="hidden-xs">?</td>
                            <td id="car-val" class="hidden-xs">?</td>
                            <td id="fa-val" class="hidden-xs">?</td>
                            <td id="df-val" class="hidden-xs">?</td>
                        </tr>
                    </tbody>
                </table>
            </div>
            <!-- /nutri-calc -->
            <div id="nutri-table" class="page-header">
                <h3>Bảng Thành Phần Dinh Dưỡng <small>Đơn vị tính: 100 gam</small></h3>
            </div>
            <div id="filter-button-group" class="m-b-1">
                <span class="label label-primary cursor-pointer" data-filter="all" onClick="foodFilter(this);">Tất Cả</span>
            </div>
            <table class="table table-striped">
                <thead>
                    <tr>
                        <th>#</th>
                        <th>Tên Thực Phẩm</th>
                        <th>Năng Lượng (kcal)</th>
                        <th class="hidden-xs">Nước (g)</th>
                        <th class="hidden-xs">Đạm (g)</th>
                        <th class="hidden-xs">Tinh Bột (g)</th>
                        <th class="hidden-xs">Béo (g)</th>
                        <th class="hidden-xs">Xơ (g)</th>
                    </tr>
                </thead>
                <tbody id="nutri-table-content"></tbody>
            </table>
            <!-- /nutri-table -->
        </div>
    </div>
    <hr>
    <footer>
        <p>© 2017 Calo-Calc v1.1 bởi <a href="https://www.inithtml.com/">Init HTML</a>.</p>
    </footer>
</div>
<!-- /container -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</body>
</html>

jQuery

Đây là phần quan trọng nhất, chúng ta sử dụng $.get để lấy dữ liệu từ JSON để xử lý. Bạn thêm đoạn mã sau vào trước </body>.

<script type="text/javascript">
    $(document).ready(function() {
        // Vars
        window.dataArray = [];
        window.allFoods = "";
        var indexFoodSelect = 1;
        var allWeight = 0;
        var allCalo = 0;
        var allWater = 0;
        var allProtein = 0;
        var allCarb = 0;
        var allFat = 0;
        var allDf = 0;
        
        // Get foods
        $.get('data/data.json', function(data) {
            dataArray = data;
            for (var i = 0; i < data.length; i++) {
                var resultHTML = "";
                resultHTML += "<tr>";
                resultHTML += "<th>" + (i + 1) + "</th>";
                resultHTML += "<td><span class=\"cursor-pointer btn-link\" data-id=" + i + " onClick=\"addToTextbox(this);\">" + data[i].name + "</span></td>";
                resultHTML += "<td>" + data[i].calo + "</td>";
                resultHTML += "<td class=\"hidden-xs\">" + data[i].water + "</td>";
                resultHTML += "<td class=\"hidden-xs\">" + data[i].protein + "</td>";
                resultHTML += "<td class=\"hidden-xs\">" + data[i].carb + "</td>";
                resultHTML += "<td class=\"hidden-xs\">" + data[i].fat + "</td>";
                resultHTML += "<td class=\"hidden-xs\">" + data[i].df + "</td>";
                resultHTML += "</tr>";
                $('#nutri-table-content').append(resultHTML);
                allFoods += resultHTML;
            }
        });
        
        // Get types
        $.get('data/type.json', function(data) {
            for (var i = 0; i < data.length; i++) {
                var resultHTML = "";
                var labelClass = "";
                var randomNumber = Math.floor((Math.random() * 5) + 1);
                if (randomNumber == 1) {
                    labelClass = "default";
                } else if (randomNumber == 2) {
                    labelClass = "success";
                } else if (randomNumber == 3) {
                    labelClass = "info";
                } else if (randomNumber == 4) {
                    labelClass = "warning";
                } else {
                    labelClass = "danger";
                }
                resultHTML += "<span class=\"label label-" + labelClass + " cursor-pointer\" data-filter=\"" + data[i].type + "\" onClick=\"foodFilter(this);\">" + data[i].type + "</span> ";
                $('#filter-button-group').append(resultHTML);
            }
        });
        
        // All selected food to result table
        $('#food-submit').click(function() {
            var weight = $('#food-weight').val();
            var indexInArray = $('#food-name').data('id');
            if (indexInArray === false) {
                $('#food-input').addClass('has-error');
            } else if (weight == '' || weight <= 0 || weight == null) {
                $('#weight-input').addClass('has-error');
                $('#food-weight').focus();
            } else {
                $('#weight-input').removeClass('has-error');
                $('#food-input').removeClass('has-error');
                var resultFoodSelect = "";
                allWeight += parseFloat(weight);
            
                var caloValue = parseFloat(dataArray[indexInArray].calo) / 100 * parseFloat(weight);
                allCalo += caloValue;
                var waterValue = parseFloat(dataArray[indexInArray].water) / 100 * parseFloat(weight);
                allWater += waterValue;
                var proteinValue = parseFloat(dataArray[indexInArray].protein) / 100 * parseFloat(weight);
                allProtein += proteinValue;
                var carbValue = parseFloat(dataArray[indexInArray].carb) / 100 * parseFloat(weight);
                allCarb += carbValue;
                var fatValue = parseFloat(dataArray[indexInArray].fat) / 100 * parseFloat(weight);
                allFat += fatValue;
                var dfValue = parseFloat(dataArray[indexInArray].df) / 100 * parseFloat(weight);
                allDf += dfValue;

                resultFoodSelect += "<tr>";
                resultFoodSelect += "<th>" + indexFoodSelect + "</th>";
                resultFoodSelect += "<td>" + dataArray[indexInArray].name + "</td>";
                resultFoodSelect += "<td>" + parseFloat(weight).toFixed(1) + "</td>";
                resultFoodSelect += "<td>" + caloValue.toFixed(1) + "</td>";
                resultFoodSelect += "<td class=\"hidden-xs\">" + waterValue.toFixed(1) + "</td>";
                resultFoodSelect += "<td class=\"hidden-xs\">" + proteinValue.toFixed(1) + "</td>";
                resultFoodSelect += "<td class=\"hidden-xs\">" + carbValue.toFixed(1) + "</td>";
                resultFoodSelect += "<td class=\"hidden-xs\">" + fatValue.toFixed(1) + "</td>";
                resultFoodSelect += "<td class=\"hidden-xs\">" + dfValue.toFixed(1) + "</td>";
                resultFoodSelect += "</tr>";
                indexFoodSelect++;
                $('#nutri-table-select').append(resultFoodSelect);
                $('#we-val').html(parseFloat(allWeight).toFixed(1));
                $('#ca-val').html("<span class=\"text-danger\">" + parseFloat(allCalo).toFixed(1) + "</span>");
                $('#wa-val').html(parseFloat(allWater).toFixed(1));
                $('#pro-val').html(parseFloat(allProtein).toFixed(1));
                $('#car-val').html(parseFloat(allCarb).toFixed(1));
                $('#fa-val').html(parseFloat(allFat).toFixed(1));
                $('#df-val').html(parseFloat(allDf).toFixed(1));   
            }
        });
    });
    
    // Add selected food to textbox
    function addToTextbox(obj) {
        $('#food-input').removeClass('has-error');
        $('#food-name').val($(obj).html());
        $('#food-name').data('id', $(obj).data('id'));
        $('html, body').animate({scrollTop: 0}, 400);
    }
    
    // Filter foods list
    function foodFilter(obj) {
        if ($(obj).data('filter') == 'all') {
            $('#nutri-table-content').html(allFoods);
        } else {
            var type = $(obj).data('filter');
            var resultHTML = "";
            var filterIndex = 1;
            for (var i = 0; i < dataArray.length; i++) {
                if (dataArray[i].type == type) {
                    resultHTML += "<tr>";
                    resultHTML += "<th>" + filterIndex + "</th>";
                    resultHTML += "<td><span class=\"cursor-pointer btn-link\" data-id=" + i + " onClick=\"addToTextbox(this);\">" + dataArray[i].name + "</span></td>";
                    resultHTML += "<td>" + dataArray[i].calo + "</td>";
                    resultHTML += "<td class=\"hidden-xs\">" + dataArray[i].water + "</td>";
                    resultHTML += "<td class=\"hidden-xs\">" + dataArray[i].protein + "</td>";
                    resultHTML += "<td class=\"hidden-xs\">" + dataArray[i].carb + "</td>";
                    resultHTML += "<td class=\"hidden-xs\">" + dataArray[i].fat + "</td>";
                    resultHTML += "<td class=\"hidden-xs\">" + dataArray[i].df + "</td>";
                    resultHTML += "</tr>";
                    filterIndex++;
                }
            }
            $('#nutri-table-content').html(resultHTML);
        }
    }
    
    // Scroll to ID
    $(function() {
  	$('a[href*="#"]:not([href="#"])').click(function() {
   	    if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
      		var target = $(this.hash);
     		target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
      		if (target.length) {
        	    $('html, body').animate({
          		scrollTop: target.offset().top - 70
        	    }, 700);
                    return false;
      		}
    	    }
  	});
    });
</script>

Vào http://localhost/calo-calc/ để xem kết quả, tải về tại đây để so sánh.

Tải về Xem demo

Chúc các bạn thành công!