Skip to content
beginner Phase 3 · HTML & CSS

JavaScript ES6+ Features and DOM Manipulation

Master modern JavaScript (ES6+) features, DOM manipulation, fetch API, modules, async/await, and JavaScript in the Magento context.

45m
0 problems
Topic Progress 0%

ES6+ JavaScript Features

Arrow Functions

// Traditional function
function add(a, b) {
    return a + b;
}

// Arrow function
const add = (a, b) => a + b;

// Single parameter (no parentheses needed)
const double = x => x * 2;

// Multiple statements need curly braces and return
const calculate = (price, qty) => {
    const subtotal = price * qty;
    const tax = subtotal * 0.1;
    return subtotal + tax;
};

Destructuring

// Object destructuring
const product = {
    name: 'Widget Pro',
    price: 29.99,
    sku: 'WDG-001',
    stock: 150
};

const { name, price, stock } = product;
console.log(name);  // 'Widget Pro'
console.log(price); // 29.99

// Renaming variables
const { name: productName, price: productPrice } = product;

// Default values
const { name, weight = 0.5 } = product; // weight defaults to 0.5

// Array destructuring
const colors = ['red', 'green', 'blue'];
const [first, second, third] = colors;
console.log(first); // 'red'

// Skip elements
const [, , last] = colors;
console.log(last); // 'blue'

// Function parameter destructuring
function displayProduct({ name, price }) {
    console.log(`${name}: $${price}`);
}
displayProduct(product); // 'Widget Pro: $29.99'

Template Literals

const name = 'Widget Pro';
const price = 29.99;

// String interpolation
const message = `${name} costs $${price.toFixed(2)}`;

// Multi-line strings
const html = `
    <div class="product">
        <h2>${name}</h2>
        <p class="price">$${price}</p>
    </div>
`;

// Expression interpolation
const discount = 10;
const finalPrice = `${name}: $${(price * (1 - discount/100)).toFixed(2)}`;

Spread and Rest Operators

// Spread operator (...)
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]

const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }

// Merge objects (later properties override earlier)
const defaults = { color: 'red', size: 'medium' };
const custom = { color: 'blue' };
const result = { ...defaults, ...custom }; // { color: 'blue', size: 'medium' }

// Rest parameters
function sum(...numbers) {
    return numbers.reduce((total, num) => total + num, 0);
}
sum(1, 2, 3, 4); // 10

Optional Chaining and Nullish Coalescing

// Optional chaining (?.)
const user = {
    name: 'John',
    address: {
        city: 'New York'
    }
};

const city = user?.address?.city;        // 'New York'
const zip = user?.address?.zip;          // undefined (no error)
const phone = user?.contact?.phone;      // undefined (no error)

// Nullish coalescing (??)
const name = user?.name ?? 'Anonymous';  // 'John'
const age = user?.age ?? 0;             // 0 (null/undefined only)

// vs logical OR (||)
const value = 0 || 'default';           // 'default' (0 is falsy)
const value2 = 0 ?? 'default';          // 0 (0 is not null/undefined)

Array Methods

const products = [
    { id: 1, name: 'Widget', price: 29.99, active: true },
    { id: 2, name: 'Gadget', price: 49.99, active: true },
    { id: 3, name: 'Doohickey', price: 19.99, active: false },
    { id: 4, name: 'Thingamajig', price: 39.99, active: true }
];

// map - transform each element
const names = products.map(p => p.name);
// ['Widget', 'Gadget', 'Doohickey', 'Thingamajig']

// filter - keep elements that pass test
const activeProducts = products.filter(p => p.active);
// [{id: 1, ...}, {id: 2, ...}, {id: 4, ...}]

// find - get first matching element
const gadget = products.find(p => p.name === 'Gadget');
// {id: 2, name: 'Gadget', ...}

// reduce - accumulate to single value
const totalPrice = products
    .filter(p => p.active)
    .reduce((sum, p) => sum + p.price, 0);
// 119.97

// some / every
const hasExpensive = products.some(p => p.price > 40);  // true
const allActive = products.every(p => p.active);         // false

DOM Manipulation and Events

Selecting Elements

// Modern selectors (preferred)
const element = document.querySelector('.product-name');           // First match
const elements = document.querySelectorAll('.product-item');       // All matches

// Get by ID (returns single element)
const header = document.getElementById('header');

// Navigate the DOM
const parent = element.parentElement;
const children = element.children;
const next = element.nextElementSibling;
const previous = element.previousElementSibling;

Modifying Elements

// Text content
element.textContent = 'New text';         // Plain text
element.innerHTML = '<b>Bold text</b>';   // HTML (be careful with XSS)

// Attributes
element.setAttribute('data-product-id', '123');
element.getAttribute('data-product-id');
element.removeAttribute('disabled');
element.hasAttribute('required');

// Classes
element.classList.add('active');
element.classList.remove('hidden');
element.classList.toggle('visible');
element.classList.contains('active');    // true/false

// Styles
element.style.color = 'red';
element.style.backgroundColor = '#fff';
element.style.display = 'none';
element.style.cssText = 'color: red; font-size: 16px;';

// Create and insert elements
const newDiv = document.createElement('div');
newDiv.classList.add('product-card');
newDiv.innerHTML = '<h3>New Product</h3>';
document.querySelector('.product-grid').appendChild(newDiv);

// Insert before existing element
const reference = document.querySelector('.product-item');
reference.parentNode.insertBefore(newDiv, reference);

Event Handling

// Add event listener
const button = document.querySelector('.add-to-cart');

button.addEventListener('click', function(event) {
    event.preventDefault();
    const productId = this.dataset.productId;
    addToCart(productId);
});

// Event object
button.addEventListener('click', (event) => {
    event.target;        // Element that triggered the event
    event.currentTarget;  // Element with the listener
    event.type;          // 'click'
    event.preventDefault();  // Prevent default action
    event.stopPropagation(); // Stop event bubbling
});

// Delegation (for dynamically added elements)
document.querySelector('.product-grid').addEventListener('click', (event) => {
    const productItem = event.target.closest('.product-item');
    if (!productItem) return;

    const productId = productItem.dataset.productId;
    console.log('Clicked product:', productId);
});

Common DOM Patterns

// Show/hide elements
toggleElement(selector) {
    document.querySelector(selector).classList.toggle('hidden');
}

// Form validation
const form = document.querySelector('#checkout-form');
form.addEventListener('submit', (event) => {
    const email = form.querySelector('#email');
    const error = form.querySelector('#email-error');

    if (!email.value.includes('@')) {
        event.preventDefault();
        error.textContent = 'Please enter a valid email';
        email.focus();
    }
});

// Infinite scroll / lazy loading
const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            loadMoreProducts();
            observer.unobserve(entry.target);
        }
    });
});

observer.observe(document.querySelector('.load-more-trigger'));

Key Takeaway

Use querySelector/querySelectorAll for element selection, addEventListener for events, and event delegation for dynamic content. Always prefer classList methods over direct className assignment.

Fetch API, Async/Await, and Magento JavaScript

Fetch API

// GET request
async function getProducts() {
    const response = await fetch('/rest/V1/products', {
        method: 'GET',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${apiToken}`
        }
    });

    if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
    }

    const data = await response.json();
    return data;
}

// POST request
async function createProduct(productData) {
    const response = await fetch('/rest/V1/products', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${apiToken}`
        },
        body: JSON.stringify({
            product: productData
        })
    });

    return await response.json();
}

// Error handling
async function safeFetch(url, options = {}) {
    try {
        const response = await fetch(url, options);

        if (!response.ok) {
            const error = await response.json();
            throw new Error(error.message || `HTTP ${response.status}`);
        }

        return await response.json();
    } catch (error) {
        console.error('Fetch error:', error);
        throw error;
    }
}

Async/Await

// Async functions always return a Promise
async function loadProduct(productId) {
    // Await pauses execution until promise resolves
    const product = await fetch(`/rest/V1/products/${productId}`)
        .then(r => r.json());

    const reviews = await fetch(`/rest/V1/products/${productId}/reviews`)
        .then(r => r.json());

    return { product, reviews };
}

// Parallel requests
async function loadDashboard() {
    const [products, orders, customers] = await Promise.all([
        fetch('/api/products').then(r => r.json()),
        fetch('/api/orders').then(r => r.json()),
        fetch('/api/customers').then(r => r.json())
    ]);

    return { products, orders, customers };
}

// Sequential with error handling
async function processOrder(orderId) {
    try {
        const order = await getOrder(orderId);
        const payment = await processPayment(order);
        await sendConfirmation(order, payment);
        return { success: true, orderId };
    } catch (error) {
        console.error('Order processing failed:', error);
        await notifyAdmin(error);
        return { success: false, error: error.message };
    }
}

Magento JavaScript (RequireJS)

Magento uses RequireJS for module loading:

// RequireJS module definition
define([
    'jquery',
    'Magento_Ui/js/modal/modal'
], function ($, modal) {
    'use strict';

    return function (config) {
        const options = {
            type: 'popup',
            responsive: true,
            innerScroll: true,
            title: config.title,
            buttons: [{
                text: $.mage.__('Close'),
                class: 'action primary close-modal',
                click: function () {
                    this.closeModal();
                }
            }]
        };

        $(config.modalSelector).modal(options);
    };
});

// Usage in PHTML
// <script type="text/x-magento-init">
// {
//     "*": {
//         "Magento_Ui/js/core/app": {
//             "component": "Vendor_Module/js/product-modal"
//         }
//     }
// }
// </script>

Magento Custom JS Component

// web/js/product-list.js
define([
    'jquery',
    'uiComponent',
    'mage/url'
], function ($, Component, urlBuilder) {
    'use strict';

    return Component.extend({
        defaults: {
            template: 'Vendor_Module/product-list',
            products: []
        },

        initialize: function () {
            this._super();
            this.loadProducts();
            return this;
        },

        loadProducts: function () {
            const apiUrl = urlBuilder.build('rest/V1/products');

            $.ajax({
                url: apiUrl,
                type: 'GET',
                dataType: 'json',
                success: function (data) {
                    this.products(data.items);
                }.bind(this)
            });
        },

        getProducts: function () {
            return this.products();
        }
    });
});

Key Takeaway

Modern JavaScript uses fetch + async/await for HTTP requests. Magento uses RequireJS for module loading and KnockoutJS for UI components. Always handle errors in async code.

Quiz

1. What is the output of: const [a, b] = [1, 2, 3]?

Question 1 options

2. What does the ?. operator do in JavaScript?

Question 2 options

3. What does async/await do?

Question 3 options

4. What module loader does Magento use?

Question 4 options

Flashcards

Question

What is destructuring in JavaScript?

Answer

Syntax for extracting values from arrays or objects into variables. const { name, price } = product; or const [first, second] = array;

Question

What does the spread operator (...) do?

Answer

Expands an iterable into individual elements. Used for: copying arrays [...arr], merging objects {...obj}, and passing arguments (...args).

Question

What is the difference between null and undefined?

Answer

undefined: variable declared but not assigned. null: intentionally empty value. Use ?? (nullish coalescing) to check for both.

Question

How do you handle errors in async/await?

Answer

Use try/catch blocks. async function foo() { try { await bar(); } catch (error) { console.error(error); } }

Question

What is event delegation?

Answer

Attaching a single event listener to a parent element instead of individual children. Works for dynamically added elements. Use event.target.closest() to find the actual target.

Question

How does Magento load JavaScript modules?

Answer

Using RequireJS. define() defines a module, require() loads it. Magento PHTML templates use data-magento-init script tags to initialize components.

Question

What does the fetch API return?

Answer

A Promise that resolves to a Response object. Use .json() to parse JSON body. Check response.ok for success. Example: const data = await fetch(url).then(r => r.json());

Question

What is template literal syntax?

Answer

Backticks (`) with ${expression} interpolation. Supports multi-line strings. Example: `Hello ${name}, you have ${count} items`.

Revision Notes

Key Takeaways

  • 1. ES6+ features: arrow functions, destructuring, template literals, spread operator, optional chaining
  • 2. DOM manipulation: querySelector, addEventListener, classList, dataset
  • 3. Fetch API + async/await for HTTP requests in modern JavaScript
  • 4. Event delegation for handling dynamically added elements
  • 5. Magento uses RequireJS for module loading and KnockoutJS for UI components
  • 6. Always handle errors with try/catch in async functions
  • 7. Optional chaining (?.) prevents null/undefined reference errors

Interview Tips

  • Explain the difference between var, let, and const
  • Describe how event delegation works and when to use it
  • Know the fetch API and how to handle errors with async/await
  • Understand how Magento loads JavaScript (RequireJS, data-magento-init)
  • Be able to explain Promises and how async/await simplifies them

Cheat Sheet

JavaScript ES6+ Cheat Sheet

Destructuring:

const { name, price } = product;
const [first, second] = array;

Arrow Functions:

const add = (a, b) => a + b;
const greet = name => `Hello ${name}`;

Template Literals:

`Hello ${name}, you have ${count} items`

Optional Chaining:

user?.address?.city  // undefined if any null
value ?? 'default'   // null/undefined only

Fetch API:

const data = await fetch(url).then(r => r.json());

Array Methods:

arr.map(x => x * 2)        // Transform
arr.filter(x => x > 0)     // Keep matching
arr.find(x => x.id === 1)  // Find first
arr.reduce((sum, x) => sum + x, 0) // Accumulate

Magento RequireJS:

define(['jquery'], function($) {
    return function(config) { /* ... */ };
});