לדלג לתוכן

3.2 בחירת ושינוי אלמנטים פתרון

פתרון - בחירת ושינוי אלמנטים

פתרון תרגיל 1

// 1. select header by id
const header = document.getElementById("main-header");
console.log("Header:", header);

// 2. select active link
const activeLink = document.querySelector(".nav-link.active");
console.log("Active link:", activeLink.textContent);

// 3. select all nav links
const allLinks = document.querySelectorAll(".nav-link");
console.log("Number of links:", allLinks.length); // 4

// 4. select all cards and print their data-category
const cards = document.querySelectorAll(".card");
cards.forEach(function(card) {
    console.log("Category:", card.dataset.category);
});

// 5. select external link using attribute selector
const externalLink = document.querySelector('a[href^="https"]');
console.log("External link:", externalLink.textContent);

// 6. find h2 of first card using DOM navigation
const firstCard = document.querySelector(".card");
const firstH2 = firstCard.firstElementChild;
console.log("First card title:", firstH2.textContent);

פתרון תרגיל 2

// 1. change h1 text
const title = document.getElementById("title");
title.textContent = "Changed Title";

// 2. change innerHTML of content
const content = document.getElementById("content");
content.innerHTML = `
    <h2>New Subtitle</h2>
    <p>First new paragraph.</p>
    <p>Second new paragraph.</p>
`;

// 3. add a list to output
const output = document.getElementById("output");
output.innerHTML = `
    <ul>
        <li>Item One</li>
        <li>Item Two</li>
        <li>Item Three</li>
    </ul>
`;

// 4. differences between textContent, innerHTML, innerText
console.log("textContent:", content.textContent);
// "New Subtitle First new paragraph. Second new paragraph."

console.log("innerHTML:", content.innerHTML);
// '<h2>New Subtitle</h2><p>First new paragraph.</p>...'

console.log("innerText:", content.innerText);
// "New Subtitle\nFirst new paragraph.\nSecond new paragraph."
  • textContent מחזיר את כל הטקסט בלי תגיות HTML
  • innerHTML מחזיר את ה-HTML הגולמי כמחרוזת
  • innerText מחזיר את הטקסט הנראה, עם שבירות שורה כמו שהם מוצגים

פתרון תרגיל 3

const box = document.getElementById("box");
const btn = document.getElementById("style-btn");

btn.addEventListener("click", function() {
    // 1. background color
    box.style.backgroundColor = "lightcoral";

    // 2. text color
    box.style.color = "white";

    // 3. border radius
    box.style.borderRadius = "15px";

    // 4. font size
    box.style.fontSize = "20px";

    // 5. box shadow
    box.style.boxShadow = "5px 5px 20px rgba(0, 0, 0, 0.3)";

    // 6. change button text
    btn.textContent = "Done!";

    // 7. disable button
    btn.disabled = true;
});

פתרון תרגיל 4

const box = document.getElementById("box");
const btnRed = document.getElementById("btn-red");
const btnBlue = document.getElementById("btn-blue");
const btnRound = document.getElementById("btn-round");
const btnShadow = document.getElementById("btn-shadow");
const btnLarge = document.getElementById("btn-large");
const btnReset = document.getElementById("btn-reset");

btnRed.addEventListener("click", function() {
    box.classList.add("red");
    box.classList.remove("blue");
});

btnBlue.addEventListener("click", function() {
    box.classList.add("blue");
    box.classList.remove("red");
});

btnRound.addEventListener("click", function() {
    box.classList.toggle("rounded");
});

btnShadow.addEventListener("click", function() {
    box.classList.toggle("shadow");
});

btnLarge.addEventListener("click", function() {
    box.classList.toggle("large");
});

btnReset.addEventListener("click", function() {
    // remove all classes except "box"
    box.className = "box";
    // alternatively:
    // box.classList.remove("red", "blue", "rounded", "shadow", "large");
});

פתרון תרגיל 5

const products = document.querySelectorAll(".product");
let totalInStock = 0;

products.forEach(function(product) {
    const name = product.querySelector("h3").textContent;
    const price = parseFloat(product.dataset.price);
    const category = product.dataset.category;
    const inStock = product.dataset.inStock === "true";

    // 1. print product info
    console.log(`${name} - $${price} - ${category} - In stock: ${inStock}`);

    // 2. calculate total of in-stock products
    if (inStock) {
        totalInStock += price;
    }

    // 3. add class for out-of-stock products
    if (!inStock) {
        product.classList.add("out-of-stock");
    }

    // 4. add discounted attribute for expensive products
    if (price > 100) {
        product.dataset.discounted = "true";
    }
});

// show total
const summary = document.getElementById("summary");
summary.textContent = "Total in stock: $" + totalInStock.toFixed(2);

פתרון תרגיל 6

// 1. get the table by id
const table = document.getElementById("users-table");

// 2. get tbody (second child of table: thead is first, tbody is second)
const tbody = table.children[1]; // or table.lastElementChild

// 3. get first row in tbody
const firstRow = tbody.firstElementChild;

// 4. print name of first user (first cell)
const firstName = firstRow.firstElementChild.textContent;
console.log("First user:", firstName); // "Alice"

// 5. iterate all rows and print names
let currentRow = tbody.firstElementChild;
while (currentRow) {
    const name = currentRow.firstElementChild.textContent;
    console.log("User:", name);
    currentRow = currentRow.nextElementSibling;
}

// 6. change background of last row
const lastRow = tbody.lastElementChild;
lastRow.style.backgroundColor = "lightyellow";

פתרון תרגיל 7

// 1. select all delete buttons
const deleteButtons = document.querySelectorAll(".delete-btn");

// 2. for each button, find its parent card and print data-id
deleteButtons.forEach(function(btn) {
    const card = btn.closest(".card");
    console.log("Button belongs to card with id:", card.dataset.id);
});

// 3. check if each card is "featured"
const allCards = document.querySelectorAll(".card");
allCards.forEach(function(card) {
    const isFeatured = card.matches(".featured");
    const id = card.dataset.id;
    console.log(`Card ${id} is featured: ${isFeatured}`);
});

// 4. from each delete button, find the cards-container
deleteButtons.forEach(function(btn) {
    const container = btn.closest(".cards-container");
    console.log("Found container:", container !== null); // true
});

פתרון תרגיל 8

const buttons = document.querySelectorAll(".theme-btn");
const title = document.getElementById("title");
const body = document.body;

buttons.forEach(function(btn) {
    btn.addEventListener("click", function() {
        const theme = btn.dataset.theme;

        // 1. change body class to the selected theme
        body.className = theme;

        // 2. move active-theme class to clicked button
        buttons.forEach(function(b) {
            b.classList.remove("active-theme");
        });
        btn.classList.add("active-theme");

        // 3. update title text
        const themeName = theme.charAt(0).toUpperCase() + theme.slice(1);
        title.textContent = "Theme: " + themeName;

        // 4. save selected theme in data attribute
        body.dataset.currentTheme = theme;
    });
});

תשובות לשאלות

  1. querySelector מול getElementById - getElementById מחפש רק לפי id וזה קצת יותר מהיר. querySelector מקבל כל סלקטור CSS ויותר גמיש. ברוב המקרים ההבדל בביצועים זניח, ואפשר להשתמש במה שנוח יותר.

  2. NodeList מול HTMLCollection - NodeList (שמוחזר מ-querySelectorAll) הוא סטטי ויש לו forEach. HTMLCollection (שמוחזר מ-getElementsByClassName) הוא חי (מתעדכן אוטומטית כשה-DOM משתנה) ואין לו forEach.

  3. classList מול style - כי classList מאפשר לנו להגדיר את כל העיצוב ב-CSS ולהחליף classes ב-JavaScript. זה יותר נקי, יותר קל לתחזוקה, ומאפשר שימוש חוזר. style מגדיר inline styles שקשה יותר לנהל.

  4. textContent מול innerHTML - textContent מחזיר ומגדיר טקסט רגיל בלי HTML. innerHTML מחזיר ומגדיר HTML כמחרוזת, כולל תגיות. textContent בטוח מפני התקפות XSS, ו-innerHTML עלול להיות מסוכן עם קלט ממשתמשים.

  5. closest מול matches - closest מחפש הורה (כולל האלמנט עצמו) שמתאים לסלקטור, ומחזיר אלמנט או null. matches רק בודק אם האלמנט עצמו מתאים לסלקטור, ומחזיר true/false. closest שימושי בטיפול באירועים כשרוצים למצוא הורה מסוים, ו-matches שימושי לסינון.