3.3 יצירה ומחיקה של אלמנטים הרצאה
יצירה של אלמנטים¶
עד עכשיו למדנו לבחור אלמנטים קיימים ולשנות אותם. עכשיו נלמד ליצור אלמנטים חדשים מאפס ולהוסיף אותם ל-DOM, וגם למחוק אלמנטים שלא צריכים יותר.
createElement - יצירת אלמנט חדש¶
המתודה document.createElement יוצרת אלמנט HTML חדש:
// create a new paragraph
const paragraph = document.createElement("p");
console.log(paragraph); // <p></p> (empty element, not in DOM yet)
// create a div
const div = document.createElement("div");
// create a button
const button = document.createElement("button");
// create an image
const img = document.createElement("img");
- האלמנט נוצר בזיכרון, אבל הוא עדיין לא בדף
- צריך להוסיף אותו ל-DOM כדי שהוא יופיע
הגדרת תוכן ותכונות על אלמנט חדש¶
אחרי שיצרנו אלמנט, נוסיף לו תוכן ותכונות:
// create and configure a paragraph
const paragraph = document.createElement("p");
paragraph.textContent = "This is a new paragraph.";
paragraph.classList.add("intro", "highlight");
paragraph.id = "my-paragraph";
// create and configure a link
const link = document.createElement("a");
link.href = "https://google.com";
link.textContent = "Go to Google";
link.target = "_blank";
link.classList.add("external-link");
// create and configure an image
const img = document.createElement("img");
img.src = "photo.jpg";
img.alt = "A nice photo";
img.width = 300;
// create and configure an input
const input = document.createElement("input");
input.type = "text";
input.placeholder = "Enter your name";
input.id = "name-input";
input.required = true;
// create and configure a button
const button = document.createElement("button");
button.textContent = "Click me!";
button.classList.add("btn", "btn-primary");
button.dataset.action = "submit";
הוספת אלמנטים ל-DOM¶
יש כמה דרכים להוסיף אלמנט שיצרנו לעץ ה-DOM.
appendChild - הוספה בסוף¶
const container = document.getElementById("container");
const newParagraph = document.createElement("p");
newParagraph.textContent = "Added at the end.";
container.appendChild(newParagraph);
// result:
// <div id="container">
// <p>Existing paragraph</p>
// <p>Added at the end.</p>
// </div>
appendChildמוסיף את האלמנט כילד אחרון- הוא מחזיר את האלמנט שנוסף
append - גרסה מודרנית ויותר גמישה¶
const container = document.getElementById("container");
// append accepts multiple arguments
const p1 = document.createElement("p");
p1.textContent = "First new paragraph";
const p2 = document.createElement("p");
p2.textContent = "Second new paragraph";
// add multiple elements at once
container.append(p1, p2);
// append can also accept plain strings
container.append("Some plain text at the end");
appendיכול לקבל כמה אלמנטים בו-זמניתappendיכול לקבל גם מחרוזות טקסט (לא רק אלמנטים)appendלא מחזיר ערך (לעומתappendChildשמחזיר את האלמנט)
prepend - הוספה בהתחלה¶
const container = document.getElementById("container");
const notice = document.createElement("p");
notice.textContent = "This will be the first child!";
notice.style.fontWeight = "bold";
container.prepend(notice);
// the notice is now the FIRST child of container
before ו-after - הוספה לפני או אחרי אלמנט¶
const middle = document.getElementById("middle");
// add element BEFORE the middle paragraph
const before = document.createElement("p");
before.textContent = "I come before!";
middle.before(before);
// add element AFTER the middle paragraph
const after = document.createElement("p");
after.textContent = "I come after!";
middle.after(after);
// result:
// <div id="container">
// <p>I come before!</p>
// <p id="middle">Middle paragraph</p>
// <p>I come after!</p>
// </div>
insertBefore - הוספה לפני אלמנט ספציפי (דרך ההורה)¶
const list = document.getElementById("list");
const item3 = document.getElementById("item-3");
// create item 2
const item2 = document.createElement("li");
item2.textContent = "Item 2";
// insert item 2 before item 3
list.insertBefore(item2, item3);
// result:
// <ul>
// <li>Item 1</li>
// <li>Item 2</li>
// <li id="item-3">Item 3</li>
// </ul>
insertBeforeנקרא על ההורה, ומקבל שני פרמטרים: האלמנט החדש, והאלמנט שלפניו להוסיף
insertAdjacentHTML - הוספת HTML כמחרוזת¶
מתודה שמאפשרת להוסיף HTML כמחרוזת בארבעה מיקומים שונים:
const target = document.getElementById("target");
// beforebegin - before the element itself
target.insertAdjacentHTML("beforebegin", "<p>Before the div</p>");
// afterbegin - inside the element, before first child
target.insertAdjacentHTML("afterbegin", "<p>First inside the div</p>");
// beforeend - inside the element, after last child
target.insertAdjacentHTML("beforeend", "<p>Last inside the div</p>");
// afterend - after the element itself
target.insertAdjacentHTML("afterend", "<p>After the div</p>");
התוצאה:
<p>Before the div</p>
<div id="target">
<p>First inside the div</p>
<p>Existing content</p>
<p>Last inside the div</p>
</div>
<p>After the div</p>
ארבעת המיקומים:
<!-- beforebegin -->
<div id="target">
<!-- afterbegin -->
<p>Existing content</p>
<!-- beforeend -->
</div>
<!-- afterend -->
- שימושי כשיש לנו HTML מוכן כמחרוזת
- יותר מהיר מ-innerHTML כי הוא לא מוחק ובונה מחדש את כל התוכן
- עדיין יש סיכון XSS אם המחרוזת מכילה קלט ממשתמש
מחיקת אלמנטים¶
remove - הדרך המודרנית¶
const toRemove = document.getElementById("to-remove");
toRemove.remove(); // gone!
// result:
// <div id="container">
// <p>I stay.</p>
// </div>
- פשוט וקל - קוראים ל-
remove()על האלמנט שרוצים להסיר
removeChild - הדרך הישנה (דרך ההורה)¶
const container = document.getElementById("container");
const toRemove = document.getElementById("to-remove");
container.removeChild(toRemove);
- צריך לגשת להורה ולקרוא לו
removeChild remove()יותר פשוט ומומלץ
מחיקת כל הילדים¶
const container = document.getElementById("container");
// option 1: set innerHTML to empty string
container.innerHTML = "";
// option 2: loop and remove
while (container.firstChild) {
container.removeChild(container.firstChild);
}
// option 3: replaceChildren with no arguments
container.replaceChildren();
החלפת אלמנטים¶
replaceWith - החלפת אלמנט בחדש¶
const oldElement = document.getElementById("old");
const newElement = document.createElement("h2");
newElement.textContent = "New and improved!";
newElement.style.color = "green";
oldElement.replaceWith(newElement);
// result:
// <div id="container">
// <h2 style="color: green;">New and improved!</h2>
// </div>
replaceChild - הדרך הישנה (דרך ההורה)¶
const container = document.getElementById("container");
const oldElement = document.getElementById("old");
const newElement = document.createElement("h2");
newElement.textContent = "New and improved!";
container.replaceChild(newElement, oldElement);
שכפול אלמנטים - cloneNode¶
<div class="template-card">
<h3>Card Title</h3>
<p>Card content goes here.</p>
<button>Click me</button>
</div>
const template = document.querySelector(".template-card");
// shallow clone - only the element itself, without children
const shallowClone = template.cloneNode(false);
console.log(shallowClone.innerHTML); // "" (empty)
// deep clone - the element and ALL its descendants
const deepClone = template.cloneNode(true);
console.log(deepClone.innerHTML); // includes h3, p, and button
// modify the clone
deepClone.querySelector("h3").textContent = "Cloned Card";
deepClone.querySelector("p").textContent = "This is a clone!";
// add the clone to the DOM
document.body.appendChild(deepClone);
cloneNode(false)- שכפול רדוד, רק האלמנט עצמוcloneNode(true)- שכפול עמוק, כולל כל הילדים- השכפול לא משכפל event listeners - צריך להוסיף אותם מחדש
DocumentFragment - שיפור ביצועים¶
כל פעם שאנחנו מוסיפים אלמנט ל-DOM, הדפדפן צריך לחשב מחדש את הפריסה ולצייר את הדף. אם מוסיפים הרבה אלמנטים אחד-אחד, זה יכול להיות איטי.
DocumentFragment הוא "מיכל" זמני שמאפשר לנו לבנות מבנה שלם ולהוסיף אותו ל-DOM בפעולה אחת:
// BAD - adding elements one by one (many DOM updates)
const list = document.getElementById("list");
for (let i = 0; i < 100; i++) {
const li = document.createElement("li");
li.textContent = "Item " + (i + 1);
list.appendChild(li); // DOM update on each iteration!
}
// GOOD - using DocumentFragment (one DOM update)
const list2 = document.getElementById("list2");
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const li = document.createElement("li");
li.textContent = "Item " + (i + 1);
fragment.appendChild(li); // adding to fragment (no DOM update)
}
list2.appendChild(fragment); // ONE DOM update for all 100 items
- ה-fragment הוא מיכל זמני שלא מופיע ב-DOM
- כשמוסיפים fragment ל-DOM, רק הילדים שלו נוספים (לא ה-fragment עצמו)
- שיפור ביצועים משמעותי כשמוסיפים הרבה אלמנטים
innerHTML מול createElement - ביצועים ואבטחה¶
שתי גישות שונות להוספת תוכן:
גישת innerHTML¶
const container = document.getElementById("container");
// quick and easy
container.innerHTML = `
<div class="card">
<h2>Card Title</h2>
<p>Card content.</p>
<button class="btn">Click</button>
</div>
`;
יתרונות:
- קצר ונוח לכתיבה
- קל לקרוא - רואים את ה-HTML ישירות
חסרונות:
- סיכון אבטחה (XSS) כשמכניסים קלט ממשתמש
- מוחק את כל התוכן הקיים ובונה מחדש
- מאבד event listeners על אלמנטים קיימים
// XSS DANGER! Never do this with user input:
const userInput = '<img src="x" onerror="alert(\'hacked!\')">';
// THIS IS DANGEROUS:
container.innerHTML = userInput; // the script will execute!
// THIS IS SAFE:
container.textContent = userInput; // displayed as plain text
גישת createElement¶
const container = document.getElementById("container");
const card = document.createElement("div");
card.classList.add("card");
const title = document.createElement("h2");
title.textContent = "Card Title";
const content = document.createElement("p");
content.textContent = "Card content.";
const button = document.createElement("button");
button.classList.add("btn");
button.textContent = "Click";
card.append(title, content, button);
container.appendChild(card);
יתרונות:
- בטוח מ-XSS - textContent מתייחס לכל דבר כטקסט רגיל
- שליטה מלאה על כל אלמנט
- לא מוחק event listeners קיימים
חסרונות:
- יותר ארוך ומפורט
- קשה יותר לקרוא כשיש מבנה מורכב
הכלל: השתמשו ב-innerHTML רק כשהנתונים בטוחים (לא קלט ממשתמש). כשיש קלט ממשתמש, השתמשו ב-createElement ו-textContent.
דוגמה מעשית - בניית רשימה דינמית¶
נבנה רשימת משימות פשוטה שמשתמשת בכל מה שלמדנו:
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<title>Todo List</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; max-width: 500px; margin: 0 auto; }
.todo-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
margin: 5px 0;
border: 1px solid #ddd;
border-radius: 5px;
}
.todo-item.done { background-color: #e8f5e9; text-decoration: line-through; color: gray; }
.delete-btn { background: #f44336; color: white; border: none; padding: 5px 10px; cursor: pointer; border-radius: 3px; }
.done-btn { background: #4CAF50; color: white; border: none; padding: 5px 10px; cursor: pointer; border-radius: 3px; margin-left: 5px; }
#add-input { padding: 8px; width: 70%; }
#add-btn { padding: 8px 16px; }
</style>
</head>
<body>
<h1>Todo List</h1>
<div>
<input type="text" id="add-input" placeholder="Enter a new task...">
<button id="add-btn">Add</button>
</div>
<div id="todo-list"></div>
<p id="counter">Tasks: 0</p>
<script>
const input = document.getElementById("add-input");
const addBtn = document.getElementById("add-btn");
const todoList = document.getElementById("todo-list");
const counter = document.getElementById("counter");
function updateCounter() {
const total = todoList.children.length;
const done = todoList.querySelectorAll(".done").length;
counter.textContent = "Tasks: " + total + " | Done: " + done;
}
function createTodoItem(text) {
// create the container
const item = document.createElement("div");
item.classList.add("todo-item");
// create the text span
const textSpan = document.createElement("span");
textSpan.textContent = text;
// create buttons container
const buttonsDiv = document.createElement("div");
// create done button
const doneBtn = document.createElement("button");
doneBtn.textContent = "Done";
doneBtn.classList.add("done-btn");
doneBtn.addEventListener("click", function() {
item.classList.toggle("done");
updateCounter();
});
// create delete button
const deleteBtn = document.createElement("button");
deleteBtn.textContent = "Delete";
deleteBtn.classList.add("delete-btn");
deleteBtn.addEventListener("click", function() {
item.remove();
updateCounter();
});
// assemble the item
buttonsDiv.append(doneBtn, deleteBtn);
item.append(textSpan, buttonsDiv);
return item;
}
function addTodo() {
const text = input.value.trim();
if (text === "") return;
const todoItem = createTodoItem(text);
todoList.appendChild(todoItem);
input.value = "";
input.focus();
updateCounter();
}
addBtn.addEventListener("click", addTodo);
input.addEventListener("keydown", function(event) {
if (event.key === "Enter") {
addTodo();
}
});
</script>
</body>
</html>
הדוגמה הזו מדגימה:
- createElement ליצירת אלמנטים חדשים
- textContent להגדרת טקסט בטוח
- classList לניהול classes
- append להרכבת מבנה מקונן
- appendChild להוספה ל-DOM
- remove למחיקה מה-DOM
- addEventListener לטיפול בלחיצות ומקלדת
- children.length ו-querySelectorAll לספירה
סיכום¶
יצירה¶
| מתודה | תיאור |
|---|---|
document.createElement("tag") |
יצירת אלמנט חדש |
document.createDocumentFragment() |
יצירת fragment לביצועים |
element.cloneNode(deep) |
שכפול אלמנט קיים |
הוספה¶
| מתודה | תיאור |
|---|---|
parent.appendChild(child) |
הוספה כילד אחרון |
parent.append(...nodes) |
הוספה בסוף (גמיש) |
parent.prepend(...nodes) |
הוספה בהתחלה |
element.before(...nodes) |
הוספה לפני האלמנט |
element.after(...nodes) |
הוספה אחרי האלמנט |
parent.insertBefore(new, ref) |
הוספה לפני אלמנט ספציפי |
element.insertAdjacentHTML(pos, html) |
הוספת HTML כמחרוזת |
מחיקה והחלפה¶
| מתודה | תיאור |
|---|---|
element.remove() |
מחיקת האלמנט |
parent.removeChild(child) |
מחיקה דרך ההורה |
element.replaceWith(new) |
החלפה באלמנט חדש |
parent.replaceChild(new, old) |
החלפה דרך ההורה |
- תמיד העדיפו
createElement+textContentעל פניinnerHTMLעם קלט ממשתמש - השתמשו ב-DocumentFragment כשמוסיפים הרבה אלמנטים
- הגרסאות המודרניות (
append,prepend,before,after,remove,replaceWith) יותר נוחות לשימוש