Skip to content

Commit 4e3a9ec

Browse files
committed
optimized leaderboard for better DOM repaint, separation of logic
1 parent a58ce8a commit 4e3a9ec

File tree

2 files changed

+141
-109
lines changed

2 files changed

+141
-109
lines changed

leaderBoard-simple/index.html

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@
2323
<div id="wrapper">
2424
<div id="leaderboard">
2525
<form action="">
26-
<input id="fname" type="text" placeholder="First Name" required autofocus />
26+
<input
27+
id="fname"
28+
type="text"
29+
placeholder="First Name"
30+
required
31+
autofocus
32+
/>
2733
<input id="lname" type="text" placeholder="Last Name" required />
2834
<select name="" id="" required>
2935
<option value="" selected disabled>Select Country</option>
@@ -33,15 +39,10 @@
3339
<option value="South Africa">South Africa</option>
3440
<option value="New Zealand">New Zealand</option>
3541
</select>
36-
<input
37-
id="score"
38-
type="text"
39-
placeholder="Enter Score"
40-
required
41-
/>
42+
<input id="score" type="text" placeholder="Enter Score" required />
4243
<button type="submit">Add</button>
4344
</form>
44-
45+
4546
<div class="board"></div>
4647
</div>
4748
</div>

leaderBoard-simple/scripts.js

Lines changed: 132 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,154 @@
1+
// --- DOM Element Selection ---
12
const form = document.querySelector("form");
2-
const firstName = document.querySelector("#fname");
3-
const lastName = document.querySelector("#lname");
4-
const country = document.querySelector("select");
5-
const score = document.querySelector("#score");
3+
const firstNameInput = document.querySelector("#fname");
4+
const lastNameInput = document.querySelector("#lname");
5+
const countryInput = document.querySelector("select");
6+
const scoreInput = document.querySelector("#score");
67
const board = document.querySelector(".board");
78

8-
form.addEventListener("submit", addRecord);
9-
9+
// --- State Management ---
1010
let leaderBoard = [];
11+
let nextId = 1;
12+
13+
// --- Functions ---
14+
15+
/**
16+
* Creates a DOM element for a single player record.
17+
* This function is purely for creating UI and does not attach event listeners.
18+
* @param {object} player - The player data object.
19+
* @returns {HTMLElement} The player record div.
20+
*/
21+
function createPlayerElement(player) {
22+
const playerRecord = document.createElement("div");
23+
playerRecord.classList.add("playerRecord");
24+
// Use a data attribute to easily identify the record later
25+
playerRecord.dataset.id = player.id;
26+
27+
const name = document.createElement("p");
28+
name.classList.add("name");
29+
name.textContent = player.name;
30+
31+
const country = document.createElement("p");
32+
country.classList.add("country");
33+
country.textContent = player.country;
34+
35+
const score = document.createElement("p");
36+
score.classList.add("score");
37+
score.textContent = player.score;
38+
39+
const icons = document.createElement("div");
40+
icons.innerHTML = `
41+
<i class="fa-solid fa-trash" data-action="delete"></i>
42+
<span class="plus-five" data-action="increase">+5</span>
43+
<span class="minus-five" data-action="decrease">-5</span>
44+
`;
45+
icons.classList.add("icons");
46+
47+
playerRecord.append(name, country, score, icons);
48+
return playerRecord;
49+
}
1150

12-
function addRecord(e) {
13-
e.preventDefault();
14-
const playerRecord = {};
15-
playerRecord["id"] = leaderBoard.length + 1;
16-
playerRecord["name"] = firstName.value + " " + lastName.value;
17-
playerRecord["country"] = country.value;
18-
playerRecord["score"] = Number(score.value);
19-
leaderBoard.push(playerRecord);
20-
21-
//SORTING LEADERBOARD
22-
sortLeaderBoard();
51+
/**
52+
* Sorts and renders the entire leaderboard to the DOM.
53+
* Uses a DocumentFragment to minimize reflows and repaints.
54+
*/
55+
function renderLeaderboard() {
56+
// Sort the data first
57+
leaderBoard.sort((a, b) => b.score - a.score);
2358

24-
//PRINTING LEADERBOARD
25-
printLeaderBoard();
59+
// Clear the board
60+
board.innerHTML = "";
2661

27-
//RESETTING THE FORM
28-
clearInputs();
62+
// Use a DocumentFragment for efficient batch updating
63+
const fragment = document.createDocumentFragment();
64+
leaderBoard.forEach((player) => {
65+
const playerElement = createPlayerElement(player);
66+
fragment.appendChild(playerElement);
67+
});
2968

30-
firstName.focus();
69+
// Append the fragment to the board in a single operation
70+
board.appendChild(fragment);
3171
}
3272

73+
/**
74+
* Resets the form input fields.
75+
*/
3376
function clearInputs() {
34-
firstName.value = "";
35-
lastName.value = "";
36-
country.value = "";
37-
score.value = "";
77+
form.reset(); // A more concise way to clear a form
78+
firstNameInput.focus();
3879
}
3980

40-
function sortLeaderBoard() {
41-
leaderBoard.sort((a, b) => {
42-
return Number(b.score) - Number(a.score);
43-
});
44-
}
81+
// --- Event Handlers ---
4582

46-
function printLeaderBoard() {
47-
board.innerText = "";
48-
leaderBoard.forEach((player) => {
49-
const playerRecord = document.createElement("div");
50-
playerRecord.classList.add("playerRecord");
51-
52-
const name = document.createElement("p");
53-
name.classList.add("name");
54-
name.innerText = player.name;
55-
56-
const country = document.createElement("p");
57-
country.classList.add("country");
58-
country.innerText = player.country;
59-
60-
const score = document.createElement("p");
61-
score.classList.add("score");
62-
score.innerText = player.score;
63-
64-
const icons = document.createElement("div");
65-
icons.classList.add("icons");
66-
67-
const trash = document.createElement("i");
68-
trash.classList.add("fa-solid", "fa-trash");
69-
trash.addEventListener("click", () => {
70-
deleteRecord(player.id);
71-
});
72-
73-
const plus = document.createElement("span");
74-
plus.innerText = "+5";
75-
plus.addEventListener("click", () => {
76-
plusMinusScore(player.id, "+");
77-
});
78-
79-
const minus = document.createElement("span");
80-
minus.innerText = "-5";
81-
minus.addEventListener("click", () => {
82-
plusMinusScore(player.id, "-");
83-
});
84-
85-
icons.append(trash);
86-
icons.append(plus);
87-
icons.append(minus);
88-
89-
playerRecord.append(name);
90-
playerRecord.append(country);
91-
playerRecord.append(score);
92-
playerRecord.append(icons);
93-
94-
board.append(playerRecord);
95-
});
96-
}
83+
/**
84+
* Handles the form submission to add a new player.
85+
* @param {Event} e - The submit event.
86+
*/
87+
function handleAddRecord(e) {
88+
e.preventDefault();
9789

98-
function deleteRecord(id) {
99-
leaderBoard = leaderBoard.filter((player) => player.id !== id);
90+
const firstName = firstNameInput.value.trim();
91+
const lastName = lastNameInput.value.trim();
92+
const country = countryInput.value;
93+
const score = Number(scoreInput.value);
10094

101-
sortLeaderBoard();
95+
if (!firstName || !lastName || !country || isNaN(score)) {
96+
alert("Please fill in all fields correctly.");
97+
return;
98+
}
10299

103-
printLeaderBoard();
100+
const newPlayer = {
101+
id: nextId++, // Use a reliable, incrementing ID
102+
name: `${firstName} ${lastName}`,
103+
country: country,
104+
score: score,
105+
};
106+
107+
leaderBoard.push(newPlayer);
108+
renderLeaderboard();
109+
clearInputs();
104110
}
105111

106-
function plusMinusScore(id, symbol) {
107-
if (symbol === "+") {
108-
leaderBoard.map((player) => {
109-
if (player.id === id) {
110-
player.score += 5;
111-
}
112-
});
113-
} else {
114-
leaderBoard.map((player) => {
115-
if (player.id === id) {
116-
player.score -= 5;
117-
}
118-
});
112+
/**
113+
* Handles clicks on the leaderboard for deleting or updating scores
114+
* using event delegation.
115+
* @param {Event} e - The click event.
116+
*/
117+
function handleBoardClick(e) {
118+
const action = e.target.dataset.action;
119+
if (!action) return; // Exit if the click was not on an action element
120+
121+
const playerRecordElement = e.target.closest(".playerRecord");
122+
if (!playerRecordElement) return;
123+
124+
const playerId = Number(playerRecordElement.dataset.id);
125+
126+
switch (action) {
127+
case "delete":
128+
leaderBoard = leaderBoard.filter((player) => player.id !== playerId);
129+
break;
130+
case "increase":
131+
leaderBoard.forEach((player) => {
132+
if (player.id === playerId) {
133+
player.score += 5;
134+
}
135+
});
136+
break;
137+
case "decrease":
138+
leaderBoard.forEach((player) => {
139+
if (player.id === playerId) {
140+
player.score -= 5;
141+
}
142+
});
143+
break;
119144
}
120-
sortLeaderBoard();
121145

122-
printLeaderBoard();
146+
// Re-render the board after any state change
147+
renderLeaderboard();
123148
}
149+
150+
// --- Event Listeners ---
151+
form.addEventListener("submit", handleAddRecord);
152+
153+
// Single event listener on the parent container for all actions
154+
board.addEventListener("click", handleBoardClick);

0 commit comments

Comments
 (0)