Understanding the Return Value
Quick Start (30 seconds)
Collections.update() returns a results object. Capture it in a variable to know exactly what happened:
const results = Collections.update({
'btn': { disabled: true },
'tag:p': { style: { color: '#374151' } }
});
console.log(results['btn'].success); // true
console.log(results['btn'].elementsUpdated); // e.g. 5 (how many buttons existed)
console.log(results['tag:p'].elementsUpdated); // e.g. 12 (how many paragraphs existed)One key per collection. Each key gives you three pieces of information: did it succeed, how many elements were updated, and a reference to those elements.
You don't have to capture it — but if you want to verify, log, or chain operations, this is how.
What Is a Return Value?
When a function finishes running, it can hand you something back. That's called the return value. In JavaScript it works like this:
// The function runs and hands something back
const results = Collections.update({ 'btn': { disabled: true } });
// ↑
// This variable catches what the function returnsIf you don't catch it, the function still runs correctly — you just don't have access to the report it generated.
Why Does This Exist?
Running Without Verification — When Fire-and-Forget Is Your Goal
For many routine updates, you don't need to inspect what happened. The update just runs:
// Simple state change — no need to inspect the results
Collections.update({
'card': { classList: { add: 'highlighted' } }
});This is perfectly valid. If all you need is for the update to happen and you trust your HTML is correct, ignoring the return value is fine.
With Verification — When Collections.update() Return Value Shines
In scenarios where you need to confirm the update reached elements, handle partial success, or chain follow-up behavior, capturing the return value provides a structured, readable way to do it:
const results = Collections.update({
'submit-btn': { disabled: false, textContent: 'Submit' },
'required-field': { setAttribute: { required: 'true' } }
});
// Verify the form is actually set up correctly before proceeding
if (results['required-field'].elementsUpdated < 3) {
console.error('Expected at least 3 required fields — form may be broken');
return;
}
console.log(`Ready: ${results['submit-btn'].elementsUpdated} submit button(s) enabled`);This approach is especially useful when: ✅ You need to guard against missing elements before taking a critical action ✅ You want to log exactly how many elements changed for debugging ✅ You need the element references for a follow-up operation ✅ You're writing production code where silent failures would be a problem
The Choice is Yours:
- Ignore the return value when the update is routine and you trust the page structure
- Capture the return value when verification, logging, or chaining is part of your logic
- Both are valid — the return value is always there if you need it
Mental Model: The Delivery Receipt
Think of Collections.update() like an online delivery service:
You place an order:
┌─────────────────────────────────────────────────────┐
│ Collections.update({ │
│ 'btn': { disabled: true } │
│ }) │
└──────────────────────────┬──────────────────────────┘
│
▼
Delivery runs:
┌─────────────────────────────────────────────────────┐
│ Library finds all .btn elements │
│ Applies disabled: true to each one │
│ Counts how many were updated │
└──────────────────────────┬──────────────────────────┘
│
▼
You receive a receipt:
┌─────────────────────────────────────────────────────┐
│ DELIVERY RECEIPT │
│ ───────────────────────────────────────────────── │
│ Order: 'btn' collection │
│ Status: ✅ Delivered Successfully │
│ Items: 5 buttons updated │
│ Tracking: [reference to those 5 elements] │
└─────────────────────────────────────────────────────┘The receipt arrives whether you pick it up or not. Capturing the return value is "picking up the receipt." The delivery happened either way.
The Structure of the Results Object
The results object mirrors exactly what you passed in. Every collection identifier you used becomes a key in the results:
What you passed in: What you get back:
────────────────────────────────────────────────────────────────
'btn': { ... } → 'btn': { success, collection, elementsUpdated }
'tag:p': { ... } → 'tag:p': { success, collection, elementsUpdated }
'name:email': { ... } → 'name:email': { success, collection, elementsUpdated }One identifier in, one result out. Always.
How Does It Work Internally?
Collections.update({
'btn': { disabled: true },
'tag:p': { style: { color: 'blue' } }
})
│
▼
1️⃣ For each collection identifier...
Process 'btn':
→ Find elements with class="btn" [btn1, btn2, btn3, btn4, btn5]
→ Apply disabled: true to each
→ Count: 5 updated
→ Build result: { success: true, collection: [...5 elements...], elementsUpdated: 5 }
Process 'tag:p':
→ Find all <p> elements [p1, p2, ..., p12]
→ Apply style.color to each
→ Count: 12 updated
→ Build result: { success: true, collection: [...12 elements...], elementsUpdated: 12 }
│
▼
2️⃣ Assemble the results object:
{
'btn': { success: true, collection: EnhancedCollection, elementsUpdated: 5 },
'tag:p': { success: true, collection: EnhancedCollection, elementsUpdated: 12 }
}
│
▼
3️⃣ Return it to the callerThe Three Properties in Each Result
Property 1: success (boolean)
results['btn'].success // true or falseWhat it tells you: Whether the update operation completed without an error.
success: true → The update ran. Elements may or may not have existed.
success: false → Something unexpected went wrong (very rare).Important nuance: An empty collection (no elements found) does NOT set success to false. The operation completed successfully — it just had nothing to update.
const results = Collections.update({
'nonexistent-class': { textContent: 'Hello' }
});
results['nonexistent-class'].success // true — operation completed
results['nonexistent-class'].elementsUpdated // 0 — nothing was there to updateThis design means you never need to try/catch for the normal case of "no elements found." That's intentional: the absence of elements is information, not an error.
Property 2: collection (EnhancedCollection)
results['btn'].collection // reference to the updated elementsWhat it is: A live reference to the actual DOM elements that were found and updated. Not a copy — the real elements. You can do further work with them directly.
const results = Collections.update({
'card': { classList: { add: 'highlighted' } }
});
// Work with the updated elements
const cards = results['card'].collection;
cards.forEach((card, index) => {
// Stagger animation timing per card
setTimeout(() => {
card.style.opacity = '1';
}, index * 100);
});When is it useful?
Use the collection reference when you need to do something that varies per element, or when you want to chain follow-up operations on exactly the elements that were just updated.
Property 3: elementsUpdated (number)
results['btn'].elementsUpdated // 0, 1, 5, 20, ...What it tells you: Exactly how many elements matched the collection identifier and received the update.
Count │ What it means
───────┼────────────────────────────────────────────────
0 │ No elements matched — the collection was empty
1 │ Exactly one element found and updated
5+ │ Multiple elements updatedThis is the most useful property for verification and debugging.
Six Practical Patterns
Pattern 1: Verify a critical update happened
const results = Collections.update({
'notification': { style: { display: 'block' } }
});
if (results['notification'].elementsUpdated === 0) {
console.warn('No notification elements on the page — check your HTML');
} else {
console.log(`Showing ${results['notification'].elementsUpdated} notification(s)`);
}Pattern 2: Log all results for debugging
const results = Collections.update({
'btn': { disabled: true },
'tag:input': { disabled: true },
'tag:select': { disabled: true }
});
// Print a summary of everything that changed
Object.entries(results).forEach(([selector, result]) => {
console.log(`${selector}: ${result.elementsUpdated} element(s) updated`);
});
// Output:
// btn: 8 element(s) updated
// tag:input: 12 element(s) updated
// tag:select: 3 element(s) updatedPattern 3: Guard against structural problems
const results = Collections.update({
'required-field': {
setAttribute: { required: 'true', 'aria-required': 'true' }
}
});
const count = results['required-field'].elementsUpdated;
if (count < 3) {
console.error(
`Expected at least 3 required fields, found ${count}. ` +
'The form structure may be incorrect.'
);
return false; // abort the operation
}
return true; // form is correctly structured, proceedPattern 4: Chain follow-up behavior on the updated elements
const results = Collections.update({
'product-card': { classList: { add: 'active' } }
});
// Now animate each card with a stagger
const cards = results['product-card'].collection;
const count = results['product-card'].elementsUpdated;
cards.forEach((card, index) => {
setTimeout(() => {
card.style.opacity = '1';
card.style.transform = 'translateY(0)';
}, index * 80);
});
console.log(`Animating ${count} product cards`);Pattern 5: Partial success handling
const results = Collections.update({
'primary-content': { style: { display: 'block' } },
'secondary-content': { style: { display: 'block' } },
'tertiary-content': { style: { display: 'block' } }
});
// Check which sections actually exist and log anything missing
const sections = Object.entries(results);
const missing = sections.filter(([key, result]) => result.elementsUpdated === 0);
if (missing.length > 0) {
const missingKeys = missing.map(([key]) => key).join(', ');
console.warn(`These sections were not found: ${missingKeys}`);
}
const updated = sections.filter(([key, result]) => result.elementsUpdated > 0);
console.log(`Updated ${updated.length} of ${sections.length} sections`);Pattern 6: User-facing feedback using the count
function clearAllNotifications() {
const results = Collections.update({
'notification': { style: { display: 'none' } }
});
const count = results['notification'].elementsUpdated;
if (count === 0) {
console.log('No notifications to clear');
} else {
console.log(`Cleared ${count} notification${count === 1 ? '' : 's'}`);
// or show a toast: "3 notifications dismissed"
}
}The Empty Collection Case
It is worth dedicating a section to this because it surprises people the first time they encounter it.
When no elements match, you still get a successful result:
const results = Collections.update({
'error-banner': { style: { display: 'block' } }
});
// If no elements with class="error-banner" exist:
results['error-banner'].success // true ← NOT false
results['error-banner'].elementsUpdated // 0
// results may also contain a warning string for the developerWhy is this considered success?
Because the operation itself completed correctly. There were simply no elements to update. This is not a failure — it's information. The page is in a state where no error banners exist, which might be perfectly normal.
How to distinguish the cases:
const r = results['error-banner'];
if (!r.success) {
// The operation itself broke — rare, investigate the library usage
console.error('Update operation failed');
} else if (r.elementsUpdated === 0) {
// The operation worked fine, but the collection was empty
console.log('No error banners on the page (this may be expected)');
} else {
// Normal case: elements found and updated
console.log(`${r.elementsUpdated} error banner(s) shown`);
}Full Real-World Example: Form Submission
Here is a complete example that uses the return value for logging, verification, and user feedback all at once:
async function handleFormSubmit(event) {
event.preventDefault();
// Step 1: Enter loading state and capture the results
const lockResults = Collections.update({
'form-field': {
disabled: true,
style: { opacity: '0.6' }
},
'submit-btn': {
disabled: true,
textContent: 'Submitting...',
style: { backgroundColor: '#9ca3af', cursor: 'not-allowed' }
}
});
// Step 2: Log what was locked (helpful during development)
console.log('Form locked:');
console.log(` - ${lockResults['form-field'].elementsUpdated} field(s) disabled`);
console.log(` - ${lockResults['submit-btn'].elementsUpdated} submit button(s) disabled`);
// Step 3: Guard — if no submit button exists, something is wrong
if (lockResults['submit-btn'].elementsUpdated === 0) {
console.error('No submit button found — aborting submission');
return;
}
// Step 4: Perform the actual submission
try {
const response = await fetch('/api/submit', {
method: 'POST',
body: new FormData(event.target)
});
if (response.ok) {
// Step 5a: Success — unlock and show confirmation
Collections.update({
'form-field': {
disabled: false,
style: { opacity: '1' }
},
'submit-btn': {
disabled: false,
textContent: 'Submit',
style: { backgroundColor: '#3b82f6', cursor: 'pointer' }
}
});
console.log('Form submitted successfully');
}
} catch (error) {
// Step 5b: Error — unlock and show error state
const unlockResults = Collections.update({
'form-field': {
disabled: false,
style: { opacity: '1' }
},
'submit-btn': {
disabled: false,
textContent: 'Try Again',
style: { backgroundColor: '#ef4444', cursor: 'pointer' }
},
'error-message': {
style: { display: 'block' },
textContent: 'Submission failed. Please try again.'
}
});
console.log(`Error state applied to ${unlockResults['error-message'].elementsUpdated} error message(s)`);
}
}What the return value adds here:
- Development logging: you see exactly how many elements were in each group
- A structural guard: if the submit button isn't found, the function stops gracefully
- Post-error feedback: you can confirm error messages were actually shown
Best Practices
✅ Check counts for important structural elements
const results = Collections.update({
'critical-widget': { classList: { add: 'initialized' } }
});
if (results['critical-widget'].elementsUpdated === 0) {
console.warn('Critical widget not found — page may not work correctly');
}✅ Use counts for user-facing feedback
const results = Collections.update({
'selected-item': { classList: { remove: 'selected' } }
});
const count = results['selected-item'].elementsUpdated;
statusBar.textContent = count === 0
? 'Nothing was selected'
: `Deselected ${count} item${count === 1 ? '' : 's'}`;✅ Use the collection reference for individual follow-up work
const results = Collections.update({
'animated-item': { classList: { add: 'ready' } }
});
results['animated-item'].collection.forEach((item, i) => {
item.style.animationDelay = `${i * 0.1}s`;
});❌ Don't ignore results when correctness depends on finding elements
// ❌ If submit-btn doesn't exist, you'll never know
Collections.update({
'submit-btn': { disabled: false }
});
// ✅ Capture and verify for critical operations
const results = Collections.update({
'submit-btn': { disabled: false }
});
if (results['submit-btn'].elementsUpdated === 0) {
console.error('Submit button not found — form will not work');
}Comparing Return Values Across Update Methods
| Property | Elements.update() | Collections.update() | Selector.update() |
|---|---|---|---|
success | ✅ Yes | ✅ Yes | ✅ Yes |
| Element reference | element: HTMLElement | collection: EnhancedCollection | elements: EnhancedCollection |
elementsUpdated | Not provided (always 1) | ✅ Yes — the count matters | ✅ Yes — the count matters |
Elements.update() always targets exactly one element per key, so a count isn't needed. Collections.update() and Selector.update() both target potentially many elements, so elementsUpdated is included and valuable in both.
Summary
The return value is Collections.update()'s way of answering: "What actually happened?"
The three properties:
success → Did the operation complete without error? (true/false)
collection → Here are the elements that were updated. (EnhancedCollection)
elementsUpdated → How many elements received the update? (number)The simple rule:
"If you're doing something critical, check the count. If you're doing routine work, you can ignore it. The return value is always there when you need it."
Congratulations — You've Completed the Collections.update() Guide
You've covered the entire learning path:
✅ Chapter 1: What Collections.update() is and why it exists — the broadcast concept, the four identifier formats, and when to choose this method over others.
✅ Chapter 2: Understanding the basic example — the three-layer structure, reading each line, and the four common beginner mistakes.
✅ Chapter 3: The type:value format — all four formats in depth, visual comparison of how class vs tag vs name targeting differs, and the edge cases.
✅ Chapter 4: Real-world examples — 10 complete, practical scenarios from dark mode to game state management, plus 4 reusable pattern templates.
✅ Chapter 5: Update object properties — all 8 categories (text content, DOM props, style, classList, setAttribute/removeAttribute, dataset, addEventListener, DOM method calls) with full examples and the "same update → every element" principle.
✅ Chapter 6: The return value — the delivery receipt mental model, 6 practical usage patterns, and best practices for when to verify and when to skip.
You're ready to use Collections.update() confidently in production.
Where to go next:
- Explore
Selector.update()for complex CSS selector targeting - Explore
Elements.update()for individual named-element updates - Build something — the best way to solidify this is real practice