Understanding the Basic Example
Quick Start (30 seconds)
Here is the example you'll learn to read by the end of this chapter. Right now it might look like a wall of code. By the last section, you'll read it like plain English.
Collections.update({
'btn': { disabled: true },
'tag:p': { style: { lineHeight: '1.6' } },
'name:email': { placeholder: 'Enter your email' }
});This one call says:
- "Disable every element with
class="btn"" - "Set comfortable line spacing on every
<p>element" - "Add a placeholder to every input with
name="email""
Three different groups. One function call. Zero loops.
The Three-Layer Structure
Before breaking down each line, look at the overall shape of any Collections.update() call. It always has three layers:
Layer 1 — The Method Call
┌─────────────────────────────────────┐
│ Collections.update( ... ) │
└──────────────┬──────────────────────┘
│
▼
Layer 2 — The Bulk Object
┌─────────────────────────────────────┐
│ { │
│ 'btn': { ... }, │ ← one entry per collection
│ 'tag:p': { ... }, │
│ 'name:email': { ... } │
│ } │
└──────────────┬──────────────────────┘
│
▼
Layer 3 — The Update Object (inside each entry)
┌─────────────────────────────────────┐
│ { disabled: true } │ ← what changes to apply
│ { style: { lineHeight: '1.6' } } │ to every element in
│ { placeholder: '...' } │ that collection
└─────────────────────────────────────┘Every time you use Collections.update(), you're building this same three-layer structure:
- The method call wrapping everything
- The bulk object mapping collection identifiers to update objects
- The update objects describing what changes to make
Now let's go line by line.
Line 1: Class Collections (The Default)
'btn': { disabled: true }What does this line say?
In plain English: "Find every element with class="btn" and disable it."
Breaking down the key
'btn'
│
└── No prefix? Then it's a CLASS NAME by default.
Collections.update() assumes class unless told otherwise.Reading the connection between HTML and JavaScript
HTML: JavaScript key:
─────────────────────────────────────────────
<button class="btn"> 'btn'
<button class="btn primary"> 'btn' ← matches! (has "btn" among its classes)
<button class="submit"> ❌ ← no match (different class)When you write 'btn' as a key, you are asking: "Find every element where class contains the word btn."
Before and After
BEFORE (3 active buttons):
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Save │ │ Cancel │ │ Delete │
│ [clickable] │ │ [clickable] │ │ [clickable] │
└──────────────┘ └──────────────┘ └──────────────┘
After: 'btn': { disabled: true }
AFTER (3 disabled buttons):
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Save │ │ Cancel │ │ Delete │
│ [disabled] │ │ [disabled] │ │ [disabled] │
└──────────────┘ └──────────────┘ └──────────────┘One line of code. Every button with class="btn" disabled simultaneously.
The shorthand and explicit forms
You can write this line in two ways that do the exact same thing:
// Shorthand — class is the default
'btn': { disabled: true }
// Explicit — spells out "class:" prefix
'class:btn': { disabled: true }Both are correct. The shorthand is more concise. The explicit form makes the intent unmistakable. Use whichever feels clearer in your context.
Line 2: Tag Collections
'tag:p': { style: { lineHeight: '1.6' } }What does this line say?
In plain English: "Find every <p> element on the page and set its line spacing to 1.6."
Breaking down the key
'tag:p'
│ │
│ └── The HTML tag name you are targeting
└────── The 'tag:' prefix tells Collections.update():
"I want ALL elements of this tag type"The tag: prefix changes the matching rule entirely. Instead of looking for a class attribute, the library now looks for elements by their HTML tag type — every <p>, every <button>, every <img>, whatever tag you specify.
Reading the connection between HTML and JavaScript
HTML: JavaScript key:
─────────────────────────────────────────────────────
<p>First paragraph.</p> 'tag:p' ← matched
<p class="intro">Second.</p> 'tag:p' ← also matched (tag wins)
<p id="conclusion">Third.</p> 'tag:p' ← also matched
<div>A div element</div> ❌ ← NOT matched (wrong tag)Tag targeting is inclusive: it matches all elements of that tag, regardless of their classes or IDs.
What is lineHeight?
lineHeight is the CSS property for the space between lines of text — like the "line spacing" control in a word processor.
BEFORE (tight spacing):
─────────────────────────────
This paragraph has default
line spacing. The text feels
cramped when paragraphs are
long or dense.
─────────────────────────────
AFTER { lineHeight: '1.6' }:
─────────────────────────────
This paragraph now has 1.6x
line spacing. Each line has
more breathing room, making
it easier to read.
─────────────────────────────Common tag targets in real projects
Collections.update({
'tag:button': { disabled: false }, // all <button> elements
'tag:input': { disabled: true }, // all <input> elements
'tag:a': { style: { color: '#3b82f6' } }, // all <a> link elements
'tag:img': { setAttribute: { loading: 'lazy' } }, // all images
'tag:textarea': { disabled: true } // all <textarea> elements
});Line 3: Name Attribute Collections
'name:email': { placeholder: 'Enter your email' }What does this line say?
In plain English: "Find every form element that has name="email" and add placeholder hint text."
Breaking down the key
'name:email'
│ │
│ └── The value of the name attribute you are matching
└──────── The 'name:' prefix tells Collections.update():
"Match by the name attribute, not by class or tag"Reading the connection between HTML and JavaScript
HTML: JavaScript key:
──────────────────────────────────────────────────────────────
<input type="text" name="email"> 'name:email' ← matched
<input type="email" name="email"> 'name:email' ← also matched
<input type="email" name="billing-email"> ❌ ← different name
<input type="text" name="username"> ❌ ← different nameThe type attribute does not matter for name-based matching. Only the name attribute value matters.
Why do form elements have a name attribute?
When a form is submitted, the browser bundles each field's value with its name to send to the server:
Submitted data: email=alice@example.com&username=alice&password=...
───── ──────── ────────
│ │ │
name="email" name="username" name="password"The name attribute is how the server identifies which piece of data belongs to which field.
The real power: Multiple forms, one update
This is where 'name:email' becomes especially useful. When you have several forms on the same page:
<!-- Login form -->
<form id="login">
<input type="text" name="email">
</form>
<!-- Newsletter signup -->
<form id="newsletter">
<input type="email" name="email">
</form>
<!-- Contact form -->
<form id="contact">
<input type="text" name="email">
</form>One update touches all three:
Collections.update({
'name:email': {
placeholder: 'your.email@example.com',
required: true
}
});BEFORE:
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ │ │ │ │ │
│ (empty) │ │ (empty) │ │ (empty) │
└──────────────────┘ └──────────────────┘ └──────────────────┘
Login form Newsletter form Contact form
AFTER:
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ your.email@... │ │ your.email@... │ │ your.email@... │
│ (required) │ │ (required) │ │ (required) │
└──────────────────┘ └──────────────────┘ └──────────────────┘
Login form Newsletter form Contact formAll three email inputs updated from a single call.
Reading the Whole Example Together
Now that you understand each line, let's read the complete example:
Collections.update({
'btn': { disabled: true },
'tag:p': { style: { lineHeight: '1.6' } },
'name:email': { placeholder: 'Enter your email' }
});Reading it like a sentence:
"Disable every element with class
btn. Set line spacing to 1.6 on every paragraph element. Add the placeholder 'Enter your email' to every input named
Translation table:
| Key | What it means | What it targets |
|---|---|---|
'btn' | class="btn" (shorthand) | Every element wearing the btn class |
'tag:p' | all <p> elements | Every paragraph tag on the page |
'name:email' | name="email" | Every form input with that name value |
Visual summary of which elements get touched:
Your HTML page:
┌─────────────────────────────────────────────────────┐
│ <button class="btn">Save</button> ←─ 'btn' │
│ <button class="btn">Cancel</button> ←─ 'btn' │
│ <button class="btn">Delete</button> ←─ 'btn' │
│ │
│ <p>First paragraph...</p> ←─ 'tag:p' │
│ <p>Second paragraph...</p> ←─ 'tag:p' │
│ <p>Third paragraph...</p> ←─ 'tag:p' │
│ │
│ <input name="email"> ←─ 'name:email'│
│ <input name="email"> ←─ 'name:email'│
│ <input name="username"> ✗ (not matched)│
└─────────────────────────────────────────────────────┘Reading the Code Out Loud
One reliable technique for understanding any Collections.update() call is to read it like instructions in English. Here's a pattern that works:
'[collection key]': { [update] }
Read as:
"For every [what the key describes], apply [the update]."Practice:
'card': { style: { opacity: '0.5' } }
// "For every element with class 'card', set opacity to 0.5"
'tag:button': { disabled: true }
// "For every <button> element, disable it"
'name:phone': { placeholder: '+1 (555) 000-0000' }
// "For every element with name='phone', add a placeholder"
'class:alert': { classList: { add: 'visible' } }
// "For every element with class 'alert', add the 'visible' class"Once you can read the code aloud fluently, you can also write it fluently.
Common Beginner Mistakes
Mistake 1: Using CSS dot syntax for classes
// ❌ WRONG — the dot means nothing here, this looks for class="."
Collections.update({
'.btn': { disabled: true }
});
// ✅ CORRECT — just the class name, no dot
Collections.update({
'btn': { disabled: true }
});Collections.update() is not CSS. The . prefix is a CSS selector convention. Here, you write the class name directly.
The fix: Remove the dot.
Mistake 2: Forgetting 'tag:' when targeting by HTML tag
// ❌ WRONG — 'p' is treated as a class name, looks for class="p"
Collections.update({
'p': { style: { lineHeight: '1.6' } }
});
// ✅ CORRECT — 'tag:p' explicitly means the <p> tag
Collections.update({
'tag:p': { style: { lineHeight: '1.6' } }
});Without the tag: prefix, 'p' just means "find elements with class="p"" — which is almost certainly not what you want.
The fix: Add tag: before any HTML tag name.
Mistake 3: Confusing name collections with class collections
// These look similar but target entirely different elements:
'submit': { disabled: true }
// ← Targets elements with CLASS "submit"
// Matches: <button class="submit">
'name:submit': { disabled: true }
// ← Targets elements with NAME attribute "submit"
// Matches: <button name="submit">The difference is subtle in the code but significant in the DOM.
The fix: If you want the name attribute, always include name:. If you want a class, write just the name.
Mistake 4: Expecting per-element variation
// ❌ This does NOT set different text per button
Collections.update({
'btn': { textContent: 'Click Me' }
});
// Every button will say "Click Me" — not unique per buttonCollections.update() applies the same update object to every element in the group. For per-element differences, use Elements.update():
// ✅ Different text per button
Elements.update({
saveBtn: { textContent: 'Save' },
cancelBtn: { textContent: 'Cancel' },
deleteBtn: { textContent: 'Delete' }
});The Format Cheat Sheet
Keep this handy:
Format Targets Example
──────────────────────────────────────────────────────────
'name' Elements with class="name" 'btn'
'class:name' Elements with class="name" 'class:btn'
'tag:name' All elements of that tag 'tag:p'
'name:value' Elements with name="value" 'name:email'Key Takeaways
- Three-layer structure: method call → bulk object → update objects
- No prefix = class by default.
'btn'meansclass="btn". tag:prefix = target by HTML tag type.'tag:p'matches all<p>elements.name:prefix = target by name attribute.'name:email'matchesname="email".- No CSS dot syntax. Write
'btn', not'.btn'. - Same update object goes to every element in the collection.
- Read it aloud: "For every [collection], apply [update]."
Up next: A full deep dive into the type:value format — all four forms, all the edge cases, and the mental models to make it stick.