3.3 יצירה ומחיקה של אלמנטים פתרון
פתרון - יצירה ומחיקה של אלמנטים¶
פתרון תרגיל 1¶
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<title>Create Elements</title>
</head>
<body>
<button id="create-btn">Create Post</button>
<div id="output"></div>
<script>
const output = document.getElementById("output");
const createBtn = document.getElementById("create-btn");
createBtn.addEventListener("click", function() {
// create article
const article = document.createElement("article");
article.classList.add("post");
// create h2
const title = document.createElement("h2");
title.textContent = "My First Post";
// create meta paragraph
const meta = document.createElement("p");
meta.classList.add("meta");
meta.textContent = "Published on 2024-01-15";
// create content paragraph
const content = document.createElement("p");
content.classList.add("content");
content.textContent = "This is the content of my first blog post.";
// create link
const link = document.createElement("a");
link.href = "#";
link.classList.add("read-more");
link.textContent = "Read more";
// assemble
article.append(title, meta, content, link);
output.appendChild(article);
});
</script>
</body>
</html>
פתרון תרגיל 2¶
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<title>Shopping List</title>
<style>
li { padding: 5px 0; font-size: 18px; }
.delete-btn { margin-right: 10px; cursor: pointer; color: red; border: none; background: none; font-size: 16px; }
</style>
</head>
<body>
<h1>Shopping List</h1>
<input type="text" id="item-input" placeholder="Enter item...">
<button id="add-btn">Add</button>
<p id="counter">Items: 0</p>
<ul id="shopping-list"></ul>
<script>
const input = document.getElementById("item-input");
const addBtn = document.getElementById("add-btn");
const list = document.getElementById("shopping-list");
const counter = document.getElementById("counter");
function updateCounter() {
counter.textContent = "Items: " + list.children.length;
}
function addItem() {
const text = input.value.trim();
if (text === "") return;
// create list item
const li = document.createElement("li");
// create text span
const span = document.createElement("span");
span.textContent = text;
// create delete button
const deleteBtn = document.createElement("button");
deleteBtn.textContent = "X";
deleteBtn.classList.add("delete-btn");
deleteBtn.addEventListener("click", function() {
li.remove();
updateCounter();
});
li.append(deleteBtn, span);
list.appendChild(li);
input.value = "";
input.focus();
updateCounter();
}
addBtn.addEventListener("click", addItem);
input.addEventListener("keydown", function(event) {
if (event.key === "Enter") {
addItem();
}
});
</script>
</body>
</html>
פתרון תרגיל 3¶
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<title>Dynamic Table</title>
<style>
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 10px; text-align: right; }
th { background-color: #333; color: white; }
.fail-row { background-color: #ffebee; }
</style>
</head>
<body>
<div id="table-container"></div>
<script>
const students = [
{ name: "Alice", grade: 92, status: "pass" },
{ name: "Bob", grade: 45, status: "fail" },
{ name: "Charlie", grade: 78, status: "pass" },
{ name: "Diana", grade: 88, status: "pass" },
{ name: "Eve", grade: 34, status: "fail" }
];
const container = document.getElementById("table-container");
// create table
const table = document.createElement("table");
// create thead
const thead = document.createElement("thead");
const headerRow = document.createElement("tr");
const headers = ["Name", "Grade", "Status"];
headers.forEach(function(headerText) {
const th = document.createElement("th");
th.textContent = headerText;
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
table.appendChild(thead);
// create tbody using DocumentFragment
const tbody = document.createElement("tbody");
const fragment = document.createDocumentFragment();
students.forEach(function(student) {
const tr = document.createElement("tr");
if (student.status === "fail") {
tr.classList.add("fail-row");
}
const tdName = document.createElement("td");
tdName.textContent = student.name;
const tdGrade = document.createElement("td");
tdGrade.textContent = student.grade;
const tdStatus = document.createElement("td");
tdStatus.textContent = student.status;
tr.append(tdName, tdGrade, tdStatus);
fragment.appendChild(tr);
});
tbody.appendChild(fragment);
table.appendChild(tbody);
container.appendChild(table);
</script>
</body>
</html>
פתרון תרגיל 4¶
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<title>Move Items</title>
<style>
.lists { display: flex; gap: 40px; }
ul { list-style: none; padding: 0; min-height: 200px; border: 1px solid #ddd; width: 200px; }
li { padding: 10px; border-bottom: 1px solid #eee; cursor: pointer; }
li:hover { background-color: #f0f0f0; }
</style>
</head>
<body>
<div class="lists">
<div>
<h3>Available</h3>
<ul id="available">
<li>JavaScript</li>
<li>Python</li>
<li>Java</li>
<li>C++</li>
<li>Ruby</li>
</ul>
</div>
<div>
<h3>Selected</h3>
<ul id="selected"></ul>
</div>
</div>
<script>
const available = document.getElementById("available");
const selected = document.getElementById("selected");
// add click handlers to available items
available.addEventListener("click", function(event) {
if (event.target.tagName === "LI") {
// moving an element to a new parent automatically removes it from the old parent
selected.appendChild(event.target);
}
});
// add click handlers to selected items
selected.addEventListener("click", function(event) {
if (event.target.tagName === "LI") {
available.appendChild(event.target);
}
});
</script>
</body>
</html>
כשמוסיפים אלמנט שכבר קיים ב-DOM למיקום חדש (עם appendChild או append), הוא מועבר אוטומטית - לא צריך למחוק אותו ידנית מהמיקום הישן.
פתרון תרגיל 5¶
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<title>Clone Cards</title>
<style>
.card { border: 1px solid #ddd; border-radius: 8px; width: 220px; display: inline-block; margin: 10px; overflow: hidden; }
.card-image { width: 100%; height: 150px; object-fit: cover; }
.card-title { margin: 10px; }
.card-description { margin: 10px; color: #666; }
.card-btn { margin: 10px; padding: 8px 16px; cursor: pointer; }
</style>
</head>
<body>
<div id="card-template" style="display: none;">
<div class="card">
<img src="" alt="" class="card-image">
<h3 class="card-title">Title</h3>
<p class="card-description">Description</p>
<button class="card-btn">Details</button>
</div>
</div>
<div id="cards-container"></div>
<script>
const products = [
{ title: "Laptop", description: "Powerful laptop", image: "https://picsum.photos/200/150?random=1" },
{ title: "Phone", description: "Smart phone", image: "https://picsum.photos/200/150?random=2" },
{ title: "Tablet", description: "Light tablet", image: "https://picsum.photos/200/150?random=3" }
];
const template = document.querySelector("#card-template .card");
const container = document.getElementById("cards-container");
products.forEach(function(product) {
// deep clone the template
const card = template.cloneNode(true);
// update content
card.querySelector(".card-title").textContent = product.title;
card.querySelector(".card-description").textContent = product.description;
const img = card.querySelector(".card-image");
img.src = product.image;
img.alt = product.title;
// add click handler to the button
card.querySelector(".card-btn").addEventListener("click", function() {
alert("Details for: " + product.title);
});
container.appendChild(card);
});
</script>
</body>
</html>
פתרון תרגיל 6¶
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<title>insertAdjacentHTML</title>
<style>
#target { border: 2px solid blue; padding: 20px; margin: 20px 0; }
button { margin: 5px; padding: 8px 16px; }
.added { background-color: #e3f2fd; padding: 5px; margin: 3px 0; }
</style>
</head>
<body>
<div id="target">
<p>Original content</p>
</div>
<button id="btn-before-begin">beforebegin</button>
<button id="btn-after-begin">afterbegin</button>
<button id="btn-before-end">beforeend</button>
<button id="btn-after-end">afterend</button>
<button id="btn-reset">Reset</button>
<script>
const target = document.getElementById("target");
let count = 0;
// save original HTML for reset
const originalHTML = target.outerHTML;
const parent = target.parentElement;
document.getElementById("btn-before-begin").addEventListener("click", function() {
count++;
target.insertAdjacentHTML("beforebegin",
'<p class="added">beforebegin - paragraph ' + count + '</p>');
});
document.getElementById("btn-after-begin").addEventListener("click", function() {
count++;
target.insertAdjacentHTML("afterbegin",
'<p class="added">afterbegin - paragraph ' + count + '</p>');
});
document.getElementById("btn-before-end").addEventListener("click", function() {
count++;
target.insertAdjacentHTML("beforeend",
'<p class="added">beforeend - paragraph ' + count + '</p>');
});
document.getElementById("btn-after-end").addEventListener("click", function() {
count++;
target.insertAdjacentHTML("afterend",
'<p class="added">afterend - paragraph ' + count + '</p>');
});
document.getElementById("btn-reset").addEventListener("click", function() {
// remove all added elements before and after target
const added = document.querySelectorAll(".added");
added.forEach(function(el) {
el.remove();
});
// restore original content
target.innerHTML = "<p>Original content</p>";
count = 0;
});
</script>
</body>
</html>
פתרון תרגיל 7¶
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<title>Contact Manager</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; max-width: 600px; margin: 0 auto; }
.form-group { margin: 10px 0; }
.form-group input { padding: 8px; width: 200px; }
.contact-card { border: 1px solid #ddd; border-radius: 8px; padding: 15px; margin: 10px 0; }
.contact-card h3 { margin: 0 0 10px 0; }
.contact-card p { margin: 5px 0; color: #555; }
.btn { padding: 6px 12px; margin-left: 5px; cursor: pointer; border: 1px solid #ddd; border-radius: 4px; }
.btn-delete { background-color: #f44336; color: white; border: none; }
.btn-edit { background-color: #2196F3; color: white; border: none; }
</style>
</head>
<body>
<h1>Contact Manager</h1>
<div id="form">
<div class="form-group">
<input type="text" id="name-input" placeholder="Name">
</div>
<div class="form-group">
<input type="tel" id="phone-input" placeholder="Phone">
</div>
<div class="form-group">
<input type="email" id="email-input" placeholder="Email">
</div>
<button id="add-btn" class="btn">Add Contact</button>
</div>
<p id="counter">Contacts: 0</p>
<div id="contacts-container"></div>
<script>
const nameInput = document.getElementById("name-input");
const phoneInput = document.getElementById("phone-input");
const emailInput = document.getElementById("email-input");
const addBtn = document.getElementById("add-btn");
const container = document.getElementById("contacts-container");
const counter = document.getElementById("counter");
function updateCounter() {
const count = container.children.length;
counter.textContent = "Contacts: " + count;
}
function createContactCard(name, phone, email) {
const card = document.createElement("div");
card.classList.add("contact-card");
const nameEl = document.createElement("h3");
nameEl.textContent = name;
const phoneEl = document.createElement("p");
phoneEl.textContent = "Phone: " + phone;
const emailEl = document.createElement("p");
emailEl.textContent = "Email: " + email;
const buttonsDiv = document.createElement("div");
const editBtn = document.createElement("button");
editBtn.textContent = "Edit";
editBtn.classList.add("btn", "btn-edit");
editBtn.addEventListener("click", function() {
const newName = prompt("Enter new name:", nameEl.textContent);
if (newName && newName.trim() !== "") {
nameEl.textContent = newName.trim();
}
});
const deleteBtn = document.createElement("button");
deleteBtn.textContent = "Delete";
deleteBtn.classList.add("btn", "btn-delete");
deleteBtn.addEventListener("click", function() {
if (confirm("Are you sure you want to delete " + nameEl.textContent + "?")) {
card.remove();
updateCounter();
}
});
buttonsDiv.append(editBtn, deleteBtn);
card.append(nameEl, phoneEl, emailEl, buttonsDiv);
return card;
}
addBtn.addEventListener("click", function() {
const name = nameInput.value.trim();
const phone = phoneInput.value.trim();
const email = emailInput.value.trim();
if (name === "" || phone === "" || email === "") {
alert("Please fill in all fields.");
return;
}
const card = createContactCard(name, phone, email);
container.appendChild(card);
// clear inputs
nameInput.value = "";
phoneInput.value = "";
emailInput.value = "";
nameInput.focus();
updateCounter();
});
</script>
</body>
</html>
פתרון תרגיל 8¶
const data = [
{ title: "Item 1", description: "Description for item 1", tags: ["new", "sale"] },
{ title: "Item 2", description: "Description for item 2", tags: ["popular"] },
{ title: "Item 3", description: "Description for item 3", tags: ["new"] }
];
// version A: innerHTML
function renderWithInnerHTML(data) {
let html = "";
data.forEach(function(item) {
const tagsHTML = item.tags
.map(function(tag) { return '<span class="tag">' + tag + '</span>'; })
.join("");
html += `
<div class="item">
<h3>${item.title}</h3>
<p>${item.description}</p>
<div class="tags">${tagsHTML}</div>
</div>
`;
});
document.getElementById("container-a").innerHTML = html;
}
// version B: createElement
function renderWithCreateElement(data) {
const container = document.getElementById("container-b");
container.innerHTML = ""; // clear existing content
const fragment = document.createDocumentFragment();
data.forEach(function(item) {
const itemDiv = document.createElement("div");
itemDiv.classList.add("item");
const title = document.createElement("h3");
title.textContent = item.title;
const desc = document.createElement("p");
desc.textContent = item.description;
const tagsDiv = document.createElement("div");
tagsDiv.classList.add("tags");
item.tags.forEach(function(tag) {
const span = document.createElement("span");
span.classList.add("tag");
span.textContent = tag;
tagsDiv.appendChild(span);
});
itemDiv.append(title, desc, tagsDiv);
fragment.appendChild(itemDiv);
});
container.appendChild(fragment);
}
השוואה:
- innerHTML - יותר קצר ויותר קל לקרוא, אבל מסוכן אם הנתונים מגיעים ממשתמש (XSS)
- createElement - יותר ארוך אבל בטוח לגמרי, כי textContent מתייחס לכל דבר כטקסט רגיל
- במקרה הזה הנתונים הם hardcoded אז שתי הגרסאות בטוחות, אבל בפרויקט אמיתי שבו הנתונים מגיעים מהמשתמש או מ-API, עדיף createElement
תשובות לשאלות¶
-
appendChild מול append -
appendChildמקבל רק אלמנט אחד ומחזיר אותו.appendמקבל כמה אלמנטים ומחרוזות, ולא מחזיר ערך.appendיותר גמיש ומודרני. -
innerHTML מסוכן עם קלט ממשתמש כי אם המשתמש מכניס HTML עם JavaScript (למשל
<script>או<img onerror="...">), הקוד ירוץ בדפדפן. זו התקפת XSS (Cross-Site Scripting). עםtextContentאוcreateElement, הטקסט לא מפורש כ-HTML. -
cloneNode(true) מול cloneNode(false) -
cloneNode(true)שכפול עמוק שכולל את כל הצאצאים.cloneNode(false)שכפול רדוד שמשכפל רק את האלמנט עצמו, ללא ילדים. שכפול לא משכפל event listeners. -
DocumentFragment הוא מיכל זמני שמאפשר לבנות מבנה DOM שלם בזיכרון ולהוסיף אותו ל-DOM בפעולה אחת. זה חוסך עדכוני DOM מרובים ומשפר ביצועים כשמוסיפים הרבה אלמנטים.
-
הזזת אלמנט קיים - כשמוסיפים אלמנט שכבר קיים ב-DOM למיקום חדש (עם appendChild, append וכו'), הוא מועבר אוטומטית מהמיקום הישן למיקום החדש. אלמנט יכול להיות רק במקום אחד ב-DOM.