Understanding Update Objects: What You Can Change
What is an "update object"?
Throughout this guide, you've seen code like this:
Elements.update({
header: { textContent: 'Welcome!' },
button: { disabled: true }
});The part after each element/selector is called an update object — it's the set of instructions describing what changes you want to make.
Think of it like a form you fill out:
┌─────────────────────────────────────┐
│ ELEMENT CHANGE REQUEST │
│ │
│ Element: header │
│ Change textContent to: 'Welcome!' │
│ │
│ ☑ Approved │
└─────────────────────────────────────┘The update object is that form. You write down exactly what should change, and the DOM Helpers library carries it out.
This section is a complete guide to everything you can put in that form — all the different types of changes you can request.
The six categories of changes
Every change you can make falls into one of six categories:
- Basic Properties — Simple one-value changes (text, value, disabled)
- Styles — Visual appearance (colors, sizes, spacing)
- Classes — Adding/removing CSS class labels
- Attributes — HTML attributes (data-, aria-, custom attributes)
- Event Listeners — What happens when users click, hover, etc.
- Dataset — Special
data-*attributes in a cleaner format
Let's learn each one.
Category 1: Basic Properties
{
textContent: 'New text',
innerHTML: '<strong>HTML</strong> content',
value: 'input value',
disabled: true,
checked: false,
// ... any DOM property
}What are "properties"?
Every element on a webpage is actually a JavaScript object with properties — like variables attached to it.
Think of an element like a person:
- A person has a name → An element has
textContent - A person has an age → An element has a
value - A person can be awake or asleep → An element can be
disabledor not
Properties are the simple facts about an element that you can read or change.
textContent — The readable text inside an element
What it does: Changes the text that appears inside an element.
Example:
Elements.update({
header: { textContent: 'Welcome to our site!' }
});Before:
<h1 id="header">Loading...</h1>After:
<h1 id="header">Welcome to our site!</h1>The text between the opening and closing tags changed.
Important: textContent treats everything as plain text. If you write <strong>bold</strong>, it shows literally as <strong>bold</strong> on the page — not as bold text.
innerHTML — HTML content inside an element
What it does: Changes the HTML structure inside an element.
Example:
Elements.update({
message: { innerHTML: '<strong>Success!</strong> Your changes were saved.' }
});Before:
<div id="message">Loading...</div>After:
<div id="message"><strong>Success!</strong> Your changes were saved.</div>Notice the <strong> tag actually works — the word "Success!" appears bold.
When to use which:
- Use
textContentfor plain text (safer, faster) - Use
innerHTMLwhen you need formatting tags like<strong>,<em>,<br>, etc.
value — The content of an input field
What it does: Sets what's typed into an input, textarea, or select element.
Example:
Elements.update({
username: { value: 'john_doe' },
email: { value: 'john@example.com' }
});Before:
<input id="username" type="text" value="">
<input id="email" type="email" value="">After:
<input id="username" type="text" value="john_doe">
<input id="email" type="email" value="john@example.com">The input fields now have content pre-filled, as if the user typed it in.
disabled — Whether an element can be interacted with
What it does: Makes an element unusable (grayed out, can't click).
Example:
Elements.update({
submitBtn: { disabled: true },
cancelBtn: { disabled: false }
});Values:
disabled: true→ Element becomes grayed out and can't be clickeddisabled: false→ Element becomes active and clickable again
Before:
<button id="submitBtn">Submit</button> <!-- clickable -->After:
<button id="submitBtn" disabled>Submit</button> <!-- grayed out, can't click -->checked — Whether a checkbox/radio is selected
What it does: Checks or unchecks a checkbox or radio button.
Example:
Elements.update({
termsCheckbox: { checked: true },
newsletterCheckbox: { checked: false }
});Values:
checked: true→ Checkbox/radio appears selected ✅checked: false→ Checkbox/radio appears unselected ☐
"Any DOM property"
The comment // ... any DOM property means you can set almost any property that exists on an HTML element.
More examples:
{
placeholder: 'Enter your name...', // Input placeholder text
required: true, // Makes an input required
src: 'image.jpg', // Changes an image source
href: 'https://example.com', // Changes a link destination
title: 'Hover tooltip text', // Tooltip that appears on hover
readOnly: true, // Makes an input read-only (can't edit)
maxLength: 100 // Limits input to 100 characters
}If it's a property on the element, you can set it here.
Category 2: Style Updates
{
style: {
color: 'blue',
backgroundColor: '#f0f0f0',
fontSize: '16px',
padding: '10px',
// ... any CSS property (camelCase)
}
}What is style?
style is a special nested object for changing visual appearance. Everything inside the style: { } object controls how the element looks — its colors, size, spacing, borders, shadows, etc.
Important formatting rule: CSS property names in JavaScript use camelCase instead of kebab-case.
CSS (in stylesheets): JavaScript (in style object):
──────────────────────────────────────────────────────
background-color → backgroundColor
font-size → fontSize
border-radius → borderRadius
padding-top → paddingTopWhy? Because JavaScript doesn't allow dashes in property names. background-color would be read as "background minus color" (a math operation). So we remove the dash and capitalize the next letter.
Common style properties
Colors:
{
style: {
color: 'red', // Text color
backgroundColor: '#3b82f6', // Background color
borderColor: 'green' // Border color
}
}Sizes:
{
style: {
fontSize: '18px', // Text size
width: '200px', // Element width
height: '100px', // Element height
padding: '20px', // Space inside element
margin: '10px' // Space outside element
}
}Borders:
{
style: {
border: '2px solid blue', // Border all around
borderRadius: '8px', // Rounded corners
borderTop: '3px dashed red' // Border on top only
}
}Positioning and layout:
{
style: {
display: 'flex', // Change layout mode
position: 'absolute', // Positioning type
top: '10px', // Distance from top
left: '50px', // Distance from left
opacity: '0.5', // Transparency (0 = invisible, 1 = solid)
zIndex: '100' // Stacking order (higher = on top)
}
}A real example: Styling an alert box
Elements.update({
alertBox: {
style: {
backgroundColor: '#fef2f2', // Light red background
color: '#991b1b', // Dark red text
padding: '16px', // Space inside
borderLeft: '4px solid #dc2626', // Thick red left border
borderRadius: '4px', // Slightly rounded corners
fontSize: '14px', // Smaller text
marginBottom: '20px' // Space below the box
}
}
});What the user sees:
┌────────────────────────────────────────────┐
║ Warning! Please review your information │ ← Light red bg, dark red text
║ before submitting the form. │ Thick red left border
└────────────────────────────────────────────┘All those styles create a professional alert box in one update.
Category 3: Class List Operations
{
classList: {
add: ['class1', 'class2'],
remove: ['old-class'],
toggle: 'active',
replace: ['old-class', 'new-class']
}
}What is classList?
Every HTML element has a list of CSS classes attached to it — like labels or tags. The classList object lets you add, remove, or swap those classes.
<div class="card active featured"></div>
↑ ↑ ↑
Three classes attachedWhy change classes instead of styles directly?
Classes are reusable. You define the style once in your CSS file, then apply it to many elements by adding the class.
CSS:
.highlighted {
background-color: yellow;
font-weight: bold;
}JavaScript:
Elements.update({
importantText: {
classList: { add: 'highlighted' }
}
});Now importantText gets the yellow background and bold text — without writing the styles in JavaScript. You just attach the label.
add — Add one or more classes
{
classList: {
add: 'active' // Add one class
}
}
{
classList: {
add: ['active', 'visible'] // Add multiple classes
}
}Before:
<button id="btn" class="primary">Click</button>After:
<button id="btn" class="primary active visible">Click</button>The classes active and visible were added to the existing list. primary stayed.
remove — Remove one or more classes
{
classList: {
remove: 'hidden' // Remove one class
}
}
{
classList: {
remove: ['hidden', 'loading'] // Remove multiple classes
}
}Before:
<div id="content" class="container hidden loading">...</div>After:
<div id="content" class="container">...</div>The hidden and loading classes were removed. container stayed.
toggle — Flip a class on/off
{
classList: {
toggle: 'active'
}
}What "toggle" means: If the class is present, remove it. If it's not present, add it. Like a light switch — flip it and the state reverses.
Before:
<nav id="menu" class="sidebar">Menu</nav>After (first toggle):
<nav id="menu" class="sidebar active">Menu</nav> <!-- active added -->After (second toggle):
<nav id="menu" class="sidebar">Menu</nav> <!-- active removed -->Great for show/hide effects, active states, and anything that switches between two modes.
replace — Swap one class for another
{
classList: {
replace: ['old-class', 'new-class']
}
}What this does: Removes old-class and adds new-class in one action.
Before:
<div id="status" class="status-pending">Processing...</div>After:
<div id="status" class="status-complete">Processing...</div>Perfect for changing states: pending → complete, light-mode → dark-mode, etc.
Category 4: Attributes
{
setAttribute: {
'data-id': '123',
'aria-label': 'Close button'
},
removeAttribute: ['hidden', 'disabled']
}What are attributes?
Attributes are the name-value pairs written inside HTML tags.
<button id="btn" type="submit" data-action="save" aria-label="Save changes">
↑ ↑ ↑ ↑
Attributes (name="value")You've already seen attributes like id, type, class, src, href. But you can also create your own custom attributes.
setAttribute — Add or change attributes
{
setAttribute: {
'data-user-id': '42',
'aria-label': 'Navigation menu',
'tabindex': '0'
}
}Before:
<nav id="menu"></nav>After:
<nav id="menu" data-user-id="42" aria-label="Navigation menu" tabindex="0"></nav>Three new attributes were added.
Common use cases:
data-*attributes for storing custom informationaria-*attributes for accessibilitytabindexfor keyboard navigation ordertitlefor hover tooltipsaltfor image descriptions
removeAttribute — Delete attributes
{
removeAttribute: ['hidden', 'disabled']
}Before:
<div id="panel" hidden disabled>Content</div>After:
<div id="panel">Content</div>The hidden and disabled attributes are completely removed.
Why remove attributes? Some attributes work just by existing — like hidden, disabled, required. Even setting them to false doesn't remove them. You have to delete them entirely.
<!-- This is still disabled! -->
<button disabled="false">Click</button>
<!-- This works — attribute is gone -->
<button>Click</button>Category 5: Event Listeners
{
addEventListener: [
'click',
(e) => {
console.log('Clicked!', e);
}
]
}What is an event listener?
An event listener is a piece of code that waits for something to happen — like a click, hover, key press — and then runs a function.
Think of it like a security camera. The camera (listener) watches a specific area (event type). When it detects motion (event happens), it records footage (runs your function).
Basic syntax: Array format
{
addEventListener: ['eventName', functionToRun]
}Two parts:
'eventName'— What you're listening for (click, hover, etc.)functionToRun— What happens when it occurs
Example:
Elements.update({
myButton: {
addEventListener: [
'click',
(e) => {
alert('Button was clicked!');
}
]
}
});Now when someone clicks the button with id="myButton", an alert pops up.
What is (e) =>?
This is an arrow function — a short way to write functions in JavaScript.
(e) => { console.log('Clicked!', e); }
↑ ↑
│ └── What the function does
└──────────── Parameter: the event objectThe e (short for "event") is an object containing information about what happened:
- What was clicked
- Where the mouse was
- What keys were pressed
- And more...
Example using the event object:
addEventListener: [
'click',
(e) => {
console.log('You clicked:', e.target.textContent);
console.log('Mouse position:', e.clientX, e.clientY);
}
]Advanced syntax: Object format (multiple events)
{
addEventListener: {
click: (e) => console.log('Click'),
mouseenter: (e) => console.log('Mouse entered'),
mouseleave: (e) => console.log('Mouse left')
}
}This attaches three different listeners to the same element at once.
Real example: Interactive card
Elements.update({
card: {
addEventListener: {
click: (e) => {
console.log('Card clicked');
},
mouseenter: (e) => {
e.target.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)';
},
mouseleave: (e) => {
e.target.style.boxShadow = 'none';
}
}
}
});What happens:
- Click the card → Logs "Card clicked"
- Hover over card → Shadow appears
- Move mouse away → Shadow disappears
Common event types
click → User clicks the element
dblclick → User double-clicks
mouseenter → Mouse moves over the element
mouseleave → Mouse moves away from the element
keydown → User presses a key
keyup → User releases a key
input → User types in an input field
change → An input value changes
submit → A form is submitted
focus → Element receives focus
blur → Element loses focusCategory 6: Dataset
{
dataset: {
userId: '123',
action: 'submit',
value: 'custom'
}
}What is dataset?
dataset is a cleaner way to set data-* attributes. Every property you add to dataset automatically becomes a data-* attribute in the HTML.
These two are equivalent:
// Using setAttribute
setAttribute: {
'data-user-id': '123',
'data-action': 'submit'
}
// Using dataset (cleaner)
dataset: {
userId: '123',
action: 'submit'
}Notice:
dataset.userIdbecomesdata-user-id(camelCase → kebab-case)- No need to write
data-over and over
Why use data-* attributes?
They let you store custom information directly on elements for later use.
Example: Storing product IDs on buttons
Selector.update({
'button.add-to-cart': {
dataset: {
productId: '12345',
productName: 'Blue Widget',
price: '29.99'
},
addEventListener: [
'click',
(e) => {
const id = e.target.dataset.productId;
const name = e.target.dataset.productName;
const price = e.target.dataset.price;
console.log(`Adding ${name} ($${price}) to cart`);
// Add to cart logic here...
}
]
}
});What happens:
- Each "Add to Cart" button gets product info stored as
data-*attributes - When clicked, the function reads that data and knows which product was clicked
- No need for IDs or complex lookups — the information lives right on the button
Combining multiple categories
You can mix and match as many categories as you need in one update object.
Example: Complete button update
Elements.update({
submitBtn: {
// Basic properties
textContent: 'Processing...',
disabled: true,
// Styles
style: {
backgroundColor: '#9ca3af',
cursor: 'not-allowed',
opacity: '0.7'
},
// Classes
classList: {
add: 'loading',
remove: 'ready'
},
// Attributes
setAttribute: {
'aria-busy': 'true'
},
// Dataset
dataset: {
status: 'processing'
}
}
});All six categories used in one update. The button:
- Changes its text to "Processing..."
- Becomes disabled
- Gets grayed out visually
- Gains the
loadingclass, loses thereadyclass - Gets an accessibility attribute
- Stores its status in a data attribute
One call. Six types of changes. One element completely transformed.
Error Handling: What happens when things go wrong
const results = Elements.update({
existingElement: { textContent: 'Updated!' },
nonExistentElement: { textContent: 'This will fail' }
});
console.log(results);Output:
{
existingElement: {
success: true,
element: HTMLElement
},
nonExistentElement: {
success: false,
error: "Element with ID 'nonExistentElement' not found"
}
}What's happening?
One element exists and gets updated successfully (success: true). The other doesn't exist and fails (success: false) with an error message explaining why.
The important part: The update keeps going. It doesn't crash. It doesn't stop. It updates what it can, reports what failed, and moves on.
This is called graceful error handling — errors are expected and managed, not catastrophic.
Checking if all updates succeeded
const results = Elements.update({
header: { textContent: 'Welcome' },
footer: { textContent: 'Copyright 2024' },
sidebar: { textContent: 'Menu' }
});
// Check if all updates succeeded
const allSuccessful = Object.values(results).every((r) => r.success);
console.log(allSuccessful); // true or falseBreaking it down
Object.values(results)
Converts the results object into an array of just the result objects (ignoring the keys).
// From this:
{
header: { success: true, ... },
footer: { success: true, ... }
}
// To this:
[
{ success: true, ... },
{ success: true, ... }
].every((r) => r.success)
Checks if every item in the array has success: true.
[
{ success: true },
{ success: true },
{ success: true }
]
// .every(r => r.success) → true (all are true)
[
{ success: true },
{ success: false }, ← One failed
{ success: true }
]
// .every(r => r.success) → false (not all are true)The result: allSuccessful is true only if every single update worked. If even one failed, it's false.
Finding which updates failed
const results = Elements.update({
header: { textContent: 'Welcome' },
missingElement: { textContent: 'Fail' },
footer: { textContent: 'Copyright' }
});
// Get failed updates
const failed = Object.entries(results)
.filter(([_, result]) => !result.success)
.map(([id, result]) => ({ id, error: result.error }));
console.log(failed);Output:
[
{
id: 'missingElement',
error: "Element with ID 'missingElement' not found"
}
]Breaking it down
Object.entries(results)
Converts the results object into an array of [key, value] pairs.
// From this:
{
header: { success: true, ... },
missingElement: { success: false, ... }
}
// To this:
[
['header', { success: true, ... }],
['missingElement', { success: false, ... }]
].filter(([_, result]) => !result.success)
Keeps only the pairs where success is false.
The [_, result] part means: "Each item is an array with two parts. I don't care about the first part (the key), so I'll call it _. I care about the second part (the result object)."
The !result.success means "NOT success" — so it keeps failures only.
.map(([id, result]) => ({ id, error: result.error }))
Transforms each failed pair into a simple object with just the ID and error message.
// From this:
['missingElement', { success: false, error: "Element not found" }]
// To this:
{ id: 'missingElement', error: "Element not found" }Using the error information
const results = Elements.update({
/* ... */
});
const failed = Object.entries(results)
.filter(([_, result]) => !result.success)
.map(([id, result]) => ({ id, error: result.error }));
if (failed.length > 0) {
console.error('Failed updates:', failed);
// Maybe show a notification to the user
alert(`Some elements couldn't be updated: ${failed.map(f => f.id).join(', ')}`);
// Or log details for debugging
failed.forEach(f => {
console.error(`Failed to update ${f.id}: ${f.error}`);
});
}What this does:
- Runs the updates
- Checks if any failed
- If yes, logs them, shows an alert, and logs details
You can catch problems early instead of wondering why your page looks broken.
The key takeaways
Six categories of updates:
- Basic properties (
textContent,value,disabled) - Styles (
style: { color, fontSize, ... }) - Classes (
classList: { add, remove, toggle }) - Attributes (
setAttribute,removeAttribute) - Events (
addEventListener) - Dataset (
dataset: { ... })
Error handling:
- Updates don't crash — they fail gracefully
success: true/falsetells you what worked- You can check results to catch problems early
The mental model:
"An update object is a shopping list. You list everything you want changed, hand it over, and get back a receipt showing what was available and what wasn't."
You don't need to use all six categories every time. Use what you need. Mix and match. The flexibility is the point.