A while back I wanted a fast way to answer "how many calories are in this" while eating instead of after — no app to install, no account, just type the food and see a number. I figured I'd build a small searchable table over a list of common foods. There's no clever algorithm hiding in something like this. It's a filtered array rendered as rows. What actually took the time was the boring stuff: how do you store "1 bowl (200g)" as data, what happens when someone searches for a category instead of a food, and whether the number you show is even the number you meant to show.
The search box already does more than the category dropdown
The table has both a text search and a category dropdown (Staple Food, Fruit, Drinks, Vegetable, Seafood, Meat, Egg). I built them as two separate filters, applied one after the other:
let showTable = computed(() => {
let searchResult = foodCalories.value.filter((item) => {
return (
item.groupName.toUpperCase().indexOf(keyword.value.toUpperCase()) > -1 ||
item.groupName_en.toUpperCase().indexOf(keyword.value.toUpperCase()) > -1 ||
item.name.toUpperCase().indexOf(keyword.value.toUpperCase()) > -1 ||
item.name_en.toUpperCase().indexOf(keyword.value.toUpperCase()) > -1
);
});
return searchResult.filter((item) => {
if (showGroup.value == "All") return true;
return item.groupName_en == showGroup.value;
});
});
The text filter checks four fields per item, not one: the Chinese name, the English name, the Chinese category, and the English category. I added the category fields mostly so a groupName_en typo somewhere wouldn't make an item unfindable. What I didn't plan for is that typing "fruit" into the search box now returns every fruit in the table, with zero interaction with the dropdown next to it — the dropdown and the search box quietly do the same job through two different UI elements. It's not a bug exactly, it's just a filter that's wider than the label on the box suggests. Nobody's complained, but if I rebuilt this I'd probably narrow the text search to just the name fields and let the dropdown own category filtering exclusively.
Calories are stored as a string, and that decision has a shelf life
Each row looks like this:
{
name: "白米飯",
name_en: "White rice",
unit: "1碗",
unit_en: "1 bowl",
weight: "200g",
calories: "225",
groupName: "主食",
groupName_en: "Staple Food",
},
calories is a string, "225", not the number 225. That was fine as long as all I ever did with it was print it next to the name. The table has no sort feature today, and that's exactly why it's gotten away with this so far — "9" sorting after "80" because "9" > "8" lexicographically only matters the moment you try to sort. The day I add "sort by calories," every row with a triple-digit value will need parseInt first, or the ordering will look randomly wrong to anyone who doesn't know why. I left it as a string because the data came from a spreadsheet of copy-pasted nutrition labels, and normalizing every value to a real number felt like busywork for a feature that didn't exist yet. That's a fair trade until it isn't.
A <table> your browser never actually shows you
The template renders the food data twice. Once as a real HTML <table> with <thead>/<tbody>, and once as a grid of styled <div>s that's the thing you actually see and interact with:
.foodCalories__table {
height: 0;
overflow: hidden;
}
The semantic table exists purely for crawlers and screen readers — search engines and assistive tech get proper table markup with real headers, while the visible UI is a <div> grid because that's what let me do per-cell click-to-copy, a font-size toggle, and a mobile layout that reflows without fighting <table> layout rules. Two representations of the same 300+ rows, kept in sync only because they're both generated from the same v-for over the same array. If I ever changed one loop without the other, they'd silently drift apart with no error to catch it.
Where the numbers don't quite line up
The honest gotchas, since none of this is as tidy as it looks:
-
Click-to-copy doesn't always match what's on screen. The visible unit column shows the Chinese unit for both
twandcnlocales (locale == "tw" || locale == "cn" ? item.unit : item.unit_en), but the click-to-copy handler for that same cell only checkslocale == "tw". Acn-locale user sees "1碗" on screen and copies "1 bowl" to their clipboard. Small, easy to miss, and exactly the kind of thing that survives for years in a component nobody re-reads line by line. -
Calories are per labeled serving, not per 100g, and the serving unit and its gram weight are just two adjacent strings (
unit,weight) glued together for display —"1碗(200g)"— with no actual unit conversion behind them. Comparing two foods with different serving sizes means doing the per-gram math yourself. -
The category dropdown is a hand-written list, separate from the actual
groupName_envalues in the data file. If a future data update adds a new category, it's searchable by text immediately but invisible in the dropdown until someone remembers to add it there too.
None of that makes the table wrong, exactly — it makes it a spreadsheet with a search box, and it's worth knowing that's what you're looking at before you trust it for anything more precise than "roughly how much is this."
I cleaned this up into a small free tool if you want to poke at the real thing: Common Food Calorie Table. No sign-up, just search and filter over 300+ foods.
Available in other languages
- 常見食物熱量表 — 繁體中文
- 常见食物热量表 — 简体中文
- Common Food Calorie Table — English
- 食品カロリー表(300種類以上) — 日本語
- 식품 칼로리 표 — 한국어
- Tableau des Calories des Aliments Courants — Français
- Таблица калорийности продуктов — Русский
- Kalorientabelle für gängige Lebensmittel — Deutsch
- Tabel Kalori Makanan Umum — Bahasa Indonesia
- Tabla de calorías de alimentos comunes — Español
- Bảng calo thực phẩm thông thường — Tiếng Việt
- ตารางแคลอรี่อาหารทั่วไป — ไทย
- Tabela kalorii popularnych produktów spożywczych — Polski
- Yaygın Yiyecek Kalori Tablosu — Türkçe
- Tabella delle calorie dei cibi comuni — Italiano
- Tabela de calorias de alimentos comuns — Português
- Tabel met calorieën van veelvoorkomende voedingsmiddelen — Nederlands
- Таблиця калорійності популярних продуктів — Українська
Top comments (0)