Handling Experiment Data with JavaScript
Last time you built an experiment protocol webpage with HTML/CSS. Clean-looking, but that page is like a gel image "photo" โ you can look at it, but it doesn't respond.
Add JavaScript and the page comes alive. Enter OD values and it auto-calculates concentrations, get a warning if titer falls below threshold, process results from 96 samples at once. If HTML is a gel image, JavaScript is the ImageJ software that analyzes it.
Variables: Recording Data in Your Lab Notebook
A variable stores data with a label attached. It's like writing "sample concentration: 2.5 ฮผg/mL" in your lab notebook.
const geneName = "BRCA1";
const sampleConcentration = 2.5;
let experimentStatus = "in progress";
console.log(geneName); // "BRCA1"
console.log(sampleConcentration); // 2.5
console.log(experimentStatus); // "in progress"const is for values that won't change, let for values that will. A gene name doesn't change during the experiment โ const. Experiment status changes from "in progress" to "complete" โ let.
JavaScript has three basic data types:
// String โ gene names, sample labels
const proteinTarget = "EGFR";
// Number โ concentrations, OD values, temperatures
const reactionTemp = 37;
const odValue = 0.85;
// Boolean โ true/false judgments
const isPositive = true;
const hasContamination = false;
console.log(typeof proteinTarget); // "string"
console.log(typeof reactionTemp); // "number"
console.log(typeof isPositive); // "boolean"Strings use quotes, numbers are written as-is, booleans are true/false. Is a PCR result positive or negative โ exactly two possible states โ that's a boolean.
Conditionals: Judging Experiment Results
Conditionals express logic like "if there's a band it's positive, if not it's negative" โ different actions depending on the situation. It's decision-making you do daily in the lab, translated into code.
const ctValue = 28.5;
const threshold = 35;
if (ctValue < threshold) {
console.log("Positive");
} else {
console.log("Negative");
}
// Output: "Positive"
console.assert(ctValue < threshold, "Ct 28.5 is below threshold 35");For multiple conditions, use else if:
const purityRatio = 1.95;
let qualityGrade;
if (purityRatio >= 1.8 && purityRatio <= 2.0) {
qualityGrade = "Pure DNA";
} else if (purityRatio < 1.8) {
qualityGrade = "Protein contamination suspected";
} else {
qualityGrade = "RNA contamination suspected";
}
console.log(`A260/A280 = ${purityRatio} โ ${qualityGrade}`);
// Output: "A260/A280 = 1.95 โ Pure DNA"
console.assert(qualityGrade === "Pure DNA");Here === is the "exactly equal?" comparison operator. Don't confuse it with = (assigning a value). In experiments, "setting a concentration" and "measuring to verify a concentration" are completely different actions. Assignment is =, comparison is ===.
Arrays and Loops: Batch Processing Sample Lists
An array is a container holding multiple data items in order. Like bands on a gel listed left to right.
const sampleIds = ["S001", "S002", "S003", "S004", "S005"];
const odValues = [0.45, 1.23, 0.89, 0.12, 2.15];
console.log(sampleIds[0]); // "S001" (first item)
console.log(sampleIds.length); // 5 (total count)
console.log(odValues[2]); // 0.89 (third item)Array indices start at 0. Like A1 being the 0th well on a 96-well plate.
A loop applies the same process to every well in a 96-well plate. Whether you have 5 or 500 samples, one piece of code handles them all.
const geneList = ["TP53", "BRCA1", "EGFR", "KRAS", "MYC"];
for (let i = 0; i < geneList.length; i++) {
console.log(`Analysis target ${i + 1}: ${geneList[i]}`);
}
// Analysis target 1: TP53
// Analysis target 2: BRCA1
// Analysis target 3: EGFR
// Analysis target 4: KRAS
// Analysis target 5: MYC
console.assert(geneList.length === 5, "Gene list has 5 items");The for loop has three parts: starting value (let i = 0), loop condition (i < geneList.length), increment (i++). Each iteration i increases by 1, traversing the array from start to end.
Combining arrays with conditionals automates bulk sample QC:
const samples = ["S001", "S002", "S003", "S004"];
const concentrations = [2.5, 0.3, 1.8, 0.1];
const minConcentration = 0.5;
let passCount = 0;
for (let i = 0; i < samples.length; i++) {
if (concentrations[i] >= minConcentration) {
console.log(`${samples[i]}: PASS`);
passCount++;
} else {
console.log(`${samples[i]}: FAIL (re-extraction needed)`);
}
}
console.log(`Passed: ${passCount}/${samples.length}`);
// S001: PASS
// S002: FAIL (re-extraction needed)
// S003: PASS
// S004: FAIL (re-extraction needed)
// Passed: 2/4
console.assert(passCount === 2, "2 samples are above 0.5");One conditional, one loop โ manual QC checks are now automated. Even if samples grow to 100, the code stays the same.
Functions: Making Protocols Reusable
A function packages a protocol step so it can be reused. Define "calculate GC content" once, and feed in any sequence to get results.
function calculateGcContent(sequence) {
let gcCount = 0;
for (let i = 0; i < sequence.length; i++) {
if (sequence[i] === "G" || sequence[i] === "C") {
gcCount++;
}
}
return (gcCount / sequence.length) * 100;
}
const gc1 = calculateGcContent("ATGCGATCGA");
const gc2 = calculateGcContent("AAATTTAAATTT");
console.log(`ATGCGATCGA โ GC = ${gc1}%`); // 50%
console.log(`AAATTTAAATTT โ GC = ${gc2}%`); // 0%
console.assert(gc1 === 50, "ATGCGATCGA GC content is 50%");
console.assert(gc2 === 0, "AAATTTAAATTT GC content is 0%");Function components:
function calculateGcContent(sequence)โ function name and parameter: the protocol name and "what to feed in"- Code inside
{ ... }โ the actual procedure returnโ returning the result. The step where you report your data after the experimentcalculateGcContent("ATGCGATCGA")โ function call. The actual value passed to the parameter is called an argument
Dilution calculations can also be a function:
function dilutionVolume(stockConc, finalConc, finalVol) {
return (finalConc * finalVol) / stockConc;
}
const needed = dilutionVolume(10, 1, 500);
console.log(`Stock needed: ${needed} ฮผL`); // Stock needed: 50 ฮผL
console.assert(needed === 50, "C1V1 = C2V2 verified");Once you create a function, just change the numbers when concentrations change. Much faster and more error-free than punching a calculator every time.
Try It Yourself (Faded Example)
Fill in the blanks to complete a DNA sequence GC content checker.
const sequence = "ATGCGCTA";const gcThreshold = 40;let gcCount = 0;for (let i = 0; i < sequence.; i++) {if (sequence[i] === "G" || sequence[i] === "") {gcCount++;}}const gcPercent = (gcCount / sequence.length) * ;if (gcPercent >= gcThreshold) {console.log("High GC");} else {console.log("Low GC");}
Common Errors & Solutions
Q: Confusing = and ===
= assigns a value, === compares. Writing if (status = "pass") assigns instead of comparing, making it always true. Always use === inside conditionals.
Q: How to access the last array element?
For const genes = ["TP53", "BRCA1", "EGFR"], genes[3] is undefined. Since array indices start at 0, access the last element with genes[genes.length - 1].
Q: Error when trying to change a const variable
TypeError: Assignment to constant variable. โ const values cannot be changed once set. Use let for variables that need to change.
Q: Function returns undefined
Without return inside the function, no result is returned. console.log() only prints to screen โ it doesn't return a value. If you need the calculation result elsewhere, always include return.
Q: Tutorials use var โ how is it different from let/const?
var is the pre-ES6 (2015) declaration method with function scope. let/const have block scope, valid only within {}. var ignores blocks and is accessible outside, causing unexpected bugs:
if (true) {
var x = 10;
let y = 20;
}
console.log(x); // 10 (accessible outside!)
// console.log(y); // ReferenceError (outside block, inaccessible)Older tutorials using var were written before ES6. In modern JavaScript, default to const, use let only when values must change. Don't use var.
Q: What is ? : (ternary operator)?
condition ? value1 : value2 โ returns value1 if true, value2 if false. It's if/else compressed into one line:
const od = 1.85;
const result = od >= 1.0 ? "pass" : "fail";
// Same as: if (od >= 1.0) { result = "pass" } else { result = "fail" }Q: What does ! mean?
! is the logical NOT operator. Flips true to false and false to true:
const hasPermission = false;
if (!hasPermission) {
console.log("Access denied"); // This executes
}Read !variable as "if the variable is false." !isPassed means "if not passed."
Q: What is the DOM? Where does document.getElementById come from?
DOM (Document Object Model) is the tree-structure object the browser creates from HTML so JavaScript can manipulate it. document is the top of that tree, and getElementById() finds a specific element in the tree:
// HTML: <div id="result">Waiting</div>
const resultDiv = document.getElementById("result");
resultDiv.innerText = "Analysis complete!";If HTML is the "blueprint," the DOM is the "building" the browser constructed from it. JavaScript can paint walls and move furniture in this building.
Q: How are backticks (`) different from quotes?
Backticks create template literals. You can insert values directly with ${variableName} inside the string:
const gene = "TP53";
const od = 2.15;
// Quotes: string concatenation is cumbersome
const msg1 = gene + " OD value: " + od;
// Backticks: clean insertion
const msg2 = `${gene} OD value: ${od}`;
// Expressions work too
const msg3 = `30 days = ${60 * 60 * 24 * 30} seconds`;With single quotes (') or double quotes ("), ${} prints as literal text. You must use backticks (`).
Q: What are expressions and statements?
Expressions evaluate to values โ they can be stored in variables. Statements perform actions without producing values:
// Expression: becomes a value
1 + 1 // 2
od >= 1.0 // true
"pass" // "pass"
// Statement: just does something (no value)
if (true) { } // conditional
for (let i...) { } // loopFunctions with return produce a value (expression) when called; without return, they return undefined. This connects to the earlier Q&A about functions returning undefined.
Q: What is an arrow function =>?
A shorter syntax using => instead of the function keyword. Commonly used for callbacks:
// Traditional
samples.filter(function(s) {
return s.status === "pass";
});
// Arrow function โ same behavior
samples.filter((s) => {
return s.status === "pass";
});
// Even shorter โ when the body is one line, braces and return can be omitted
samples.filter(s => s.status === "pass");function(x) { return ... } and x => ... behave identically in most cases. DevBench uses the function style for beginner readability, but real-world code and AI-generated code overwhelmingly use arrow functions.
Q: Why does the whole thing stop when JavaScript hits an error?
JavaScript runs code top to bottom. When an error occurs mid-way, execution stops at that line and nothing below runs:
console.log("Line 1 printed");
console.log("Hello".indexOf); // Error occurs here
console.log("Line 3 not printed"); // Never runsSo when an error appears in the Console, check the line number in the error message first and fix that line. Code below not running isn't a problem below โ it's the error above.
Q: Quick reference for key operators
| Category | Operators | Meaning | Example | Result |
|---|---|---|---|---|
| Arithmetic | + - * / % | Add, subtract, multiply, divide, remainder | 5 % 2 | 1 |
| Comparison | === !== > < | Equal (type-safe), not equal, greater, less | 5 === "5" | false |
| Logical | && || ! | AND, OR, NOT | !true | false |
| Assignment | = += -= | Assign, add-assign, subtract-assign | x += 3 | x = x + 3 |
| Other | typeof ? : | Type check, ternary | typeof 42 | "number" |
Remember the difference between == (loose comparison) and === (strict comparison). 5 == "5" is true but 5 === "5" is false. In practice, always use === to be safe.